0.1 release, add .on(...) and .once(...) to RJSVM, add tests

This commit is contained in:
nitowa
2023-10-06 23:55:09 +02:00
parent 1dae68b1c7
commit 270c41a3dc
45 changed files with 5610 additions and 779 deletions
+9
View File
@@ -0,0 +1,9 @@
build
node_modules
.vscode
lib
docs
gui/*.js
src/gui/build
src/gui/dist
src/gui/node_modules
+9
View File
@@ -0,0 +1,9 @@
src
.git
.vscode
node_modules
test
.drone.yml
.gitignore
tsconfig.json
lib/src/xrpIO/ripple-bindings.*
-4
View File
@@ -1,4 +0,0 @@
import { RJSVM, RJSVM_Config, RJSVM_Implementations, Function_Map, Endpoints_Of, State_Of, Generic_Ctor_ReturnType } from "./types";
export declare abstract class RJSVM_Builder {
static from<Base_Ctor extends (new (...args: any) => RJSVM<State_T, Endpoints_T>) | (abstract new (...args: any) => RJSVM<State_T, Endpoints_T>), Impl extends RJSVM_Implementations<any, Endpoints_T>, Endpoints_T extends Function_Map = Endpoints_Of<Generic_Ctor_ReturnType<Base_Ctor>>, State_T = State_Of<Generic_Ctor_ReturnType<Base_Ctor>>>(Base: Base_Ctor, defs: Impl): (new (config: RJSVM_Config) => RJSVM<State_T, Endpoints_T>);
}
-127
View File
@@ -1,127 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RJSVM_Builder = void 0;
const types_1 = require("./types");
const xrpl_1 = require("xrpl");
const xrpio_1 = require("xrpio");
const dataparser_1 = require("../../util/dataparser");
const protocol_constants_1 = require("../../util/protocol.constants");
class RJSVM_Builder {
static from(Base, defs) {
return class RJSVM_Runnable extends Base {
config;
sync_block_height = -1;
rippleApi;
xrpIO;
definitions;
constructor(config) {
super();
this.config = config;
if (!protocol_constants_1.XRP_ADDRESS.test(this.owner)) {
throw new Error(`Inavlid owner address ${this.owner}`);
}
this.definitions = Object.freeze(defs);
Object.entries(this.definitions).forEach(([k, v]) => {
this[k] = (env, ...args) => v.implementation.apply(this, [env, ...args]);
});
}
connect = async () => {
this.rippleApi = new xrpl_1.Client(this.config.rippleNode);
await this.rippleApi.connect();
this.xrpIO = new xrpio_1.xrpIO(this.config.rippleNode);
await this.xrpIO.connect();
await this.sync();
this.rippleApi.on('ledgerClosed', (ledger) => {
/*
console.log("---- Ledger ----")
console.log("index",ledger.ledger_index)
console.log("hash", ledger.ledger_hash)
console.log("---- /Ledger ----")
*/
});
await this.rippleApi.request({
command: 'subscribe',
streams: ['ledger']
});
};
handlePayload = async (tx, payload) => {
if (!(payload.endpoint in this.definitions)) {
return;
}
const endpointDef = this.definitions[payload.endpoint];
if (endpointDef.visibility === 'owner' && tx.Account !== this.owner) {
console.log(`owner restricted endpoint "${payload.endpoint}" called from ${tx.hash}. But ${tx.Account} != ${this.owner}`);
return;
}
if (endpointDef.fee && Number(tx.Amount) < endpointDef.fee) {
console.log(`Insufficient fee ${tx.hash}. Required ${endpointDef.fee}, was ${tx.Amount}`);
return;
}
try {
const data = await this.xrpIO.treeRead([payload.data], tx.Account);
const jsonData = JSON.parse(data);
endpointDef.parameterSchema.parse(jsonData);
this[payload.endpoint].apply(this, [tx, jsonData]);
}
catch (err) {
console.log(err);
return;
}
};
parseMemos = async (tx, memos) => {
memos
.map((memo) => {
if (!memo.Memo || !memo.Memo.MemoData) {
return;
}
try {
const data = dataparser_1.DataParser.hex_to_ascii(memo.Memo.MemoData);
return JSON.parse(data);
}
catch (e) {
return;
}
})
.filter((data) => data != undefined)
.forEach(payload => {
try {
const parsedPayload = types_1.payloadSchema.parse(payload);
this.handlePayload(tx, parsedPayload);
}
catch (e) {
}
});
};
sync = async (marker) => {
//If `forward` is set to true, returns values indexed with the oldest ledger first.
//Otherwise, the results are indexed with the newest ledger first.
//(Each page of results may not be internally ordered, but the pages are overall ordered.)
const resp = await this.rippleApi.request({
command: "account_tx",
account: this.config.listeningAddress,
forward: true,
ledger_index_min: this.sync_block_height,
marker: marker,
});
//results may not be ordered, so sort them
const raw_txs = resp.result.transactions
.sort((a, b) => a.tx.ledger_index - b.tx.ledger_index)
.map((entry) => entry.tx);
//apply any state changes
await Promise.all(raw_txs.map((tx) => this.parseMemos(tx, tx.Memos)));
//if marker is present the result is paginated.
//re-run the same request with the marker included
if (resp.result.marker) {
await this.sync(resp.result.marker);
}
else {
//presence of no marker means we caught up to current block height
this.sync_block_height = resp.result.ledger_index_max + 1;
//schedule the next sync
setTimeout(this.sync, 10000);
}
};
};
}
}
exports.RJSVM_Builder = RJSVM_Builder;
-67
View File
@@ -1,67 +0,0 @@
import { z } from "zod";
export declare abstract class RJSVM<State_T = any, Definitions_T extends Function_Map = Function_Map> {
owner: string;
state: State_T;
sync_block_height: number;
config: RJSVM_Config;
definitions: RJSVM_Implementations<any, Definitions_T>;
connect: () => Promise<void>;
}
export type Generic_Ctor_ReturnType<Ctor> = Ctor extends new (...args: any) => infer T ? T : Ctor extends abstract new (...args: any) => infer A ? A : never;
export type State_Of<T extends RJSVM<any>> = T extends RJSVM<infer State_T> ? State_T : any;
export type Endpoints_Of<T extends RJSVM<any, any>> = T extends RJSVM<any, infer Endpoints_T> ? Endpoints_T : any;
export type PaymentTx_T = {
Account: string;
Amount: string;
Destination: string;
Fee: string;
Flags: number;
LastLedgerSequence: number;
Memos: any[];
Sequence: number;
SigningPubKey: string;
TransactionType: 'Payment';
TxnSignature: string;
date: number;
hash: string;
inLedger: number;
ledger_index: number;
};
export type ParameterizedFunction = (...args: any) => void;
export type Function_Map = {
[K in string]: ParameterizedFunction;
};
export type RJSVM_Endpoint<RJSVM_T extends RJSVM<any, Definitions_T>, Definitions_T extends Function_Map = Endpoints_Of<RJSVM_T>, Impl extends RJSVM_EndpointHandler<RJSVM_T, Definitions_T, ParameterizedFunction> = RJSVM_EndpointHandler<RJSVM_T, Definitions_T, ParameterizedFunction>> = {
implementation: Impl;
visibility: 'owner' | 'public';
fee?: number;
parameterSchema: z.Schema;
};
export type RJSVM_Implementations<RJSVM_T extends RJSVM<any, Definitions>, Definitions extends Function_Map = Endpoints_Of<RJSVM_T>> = {
[Key in keyof Definitions]: RJSVM_Endpoint<RJSVM_T, Definitions, RJSVM_EndpointHandler<RJSVM_T, Definitions, Definitions[Key]>>;
};
export type RJSVM_EndpointHandler<RJSVM_T extends RJSVM<any, Definitions_T>, Definitions_T extends Function_Map = Endpoints_Of<RJSVM_T>, Fn extends ParameterizedFunction = ParameterizedFunction> = (this: RJSVM_T & RJSVM_Interface<RJSVM_T, State_Of<RJSVM_T>, Definitions_T>, env: PaymentTx_T, ...args: Parameters<Fn>) => void;
export type RJSVM_Interface<RJSVM_T extends RJSVM<State_T, Definitions_T>, State_T = State_Of<RJSVM_T>, Definitions_T extends Function_Map = Endpoints_Of<RJSVM_T>> = {
state: State_T;
} & {
[K in keyof Definitions_T]?: RJSVM_EndpointHandler<RJSVM_T, Definitions_T, Definitions_T[K]>;
};
export type RJSVM_InitState<State_T> = {
owner: string;
state: State_T;
};
export type RJSVM_Config = {
rippleNode: string;
listeningAddress: string;
};
export declare const payloadSchema: z.ZodObject<{
endpoint: z.ZodString;
data: z.ZodString;
}, "strip", z.ZodTypeAny, {
endpoint?: string;
data?: string;
}, {
endpoint?: string;
data?: string;
}>;
export type Payload = z.infer<typeof payloadSchema>;
-17
View File
@@ -1,17 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.payloadSchema = exports.RJSVM = void 0;
const zod_1 = require("zod");
class RJSVM {
owner;
state;
sync_block_height;
config;
definitions;
connect;
}
exports.RJSVM = RJSVM;
exports.payloadSchema = zod_1.z.object({
endpoint: zod_1.z.string(),
data: zod_1.z.string()
});
-15
View File
@@ -1,15 +0,0 @@
type Wallet = {
address: string;
secret: string;
};
type DatawriterConfig = {
receiveAddress: string;
sendWallet: Wallet;
xrpNode: string;
};
export declare class Datawriter {
private config;
constructor(config: DatawriterConfig);
callEndpoint(endpointName: string, contractAddress: string, parameter: any, fee?: number): Promise<void>;
}
export {};
-53
View File
@@ -1,53 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Datawriter = void 0;
const xrpio_1 = require("xrpio");
class Datawriter {
config;
constructor(config) {
this.config = config;
}
async callEndpoint(endpointName, contractAddress, parameter, fee) {
const xrpio = new xrpio_1.xrpIO(this.config.xrpNode);
await xrpio.connect();
try {
const dataHash = await xrpio.treeWrite(JSON.stringify(parameter), this.config.receiveAddress, this.config.sendWallet.secret);
await xrpio.writeRaw({
data: JSON.stringify({
endpoint: endpointName,
data: dataHash
})
}, contractAddress, this.config.sendWallet.secret, undefined, fee ? String(fee) : undefined);
}
catch (e) {
console.log(e);
}
finally {
await xrpio.disconnect();
}
}
}
exports.Datawriter = Datawriter;
(async () => {
const owner_dw = new Datawriter({
receiveAddress: "rDuXvMYNCEJCYDMFQykcafXhgi2NvMWqR",
sendWallet: {
address: "rUsPfG1hn6w6is28p5FUDWdMwvCo1iHrYq",
secret: "sEdTMicaTVmfsVMLhxrufyAzEQSnsaP"
},
xrpNode: "wss://s.altnet.rippletest.net:51233"
});
//await owner_dw.callEndpoint('submit', 'rLaXUiYvW1EMns69PsAfwSLb2VgNtPpZwq', { title: "1", body: "2", from: "3" }, 100)
const user_dw = new Datawriter({
receiveAddress: "rwqCiEr3SLF43rAduhCChkR2K1XDhiqx5g",
sendWallet: {
address: "rHWN4X3hbodryX8H1EoPvmMV7AFHngEiBe",
secret: "sEdTdLPWvz69UAUmt1zYijTyWheER9u"
},
xrpNode: "wss://s.altnet.rippletest.net:51233"
});
//await owner_dw.callEndpoint('submit', 'rLaXUiYvW1EMns69PsAfwSLb2VgNtPpZwq', { title: "1", body: "2", from: "3" }, 100)
//await user_dw.callEndpoint('restricted', 'rLaXUiYvW1EMns69PsAfwSLb2VgNtPpZwq', { title: "user", body: "user", from: "user" })
//await owner_dw.callEndpoint('restricted', 'rLaXUiYvW1EMns69PsAfwSLb2VgNtPpZwq', { title: "owner", body: "owner", from: "owner" })
await user_dw.callEndpoint('setTns', 'rLaXUiYvW1EMns69PsAfwSLb2VgNtPpZwq', { hash: '01708abcF00636CE10E191FD782DBDC8F4076F28404BD88EBC31DE42DD084C0944B', name: 'test' });
})();
-3
View File
@@ -1,3 +0,0 @@
export * from "./RJSVM/framework/rjsvm";
export * from "./RJSVM/framework/types";
export * from "./util/dataparser";
-19
View File
@@ -1,19 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./RJSVM/framework/rjsvm"), exports);
__exportStar(require("./RJSVM/framework/types"), exports);
__exportStar(require("./util/dataparser"), exports);
-1
View File
@@ -1 +0,0 @@
export {};
-55
View File
@@ -1,55 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const zod_1 = require("zod");
const types_1 = require("./RJSVM/framework/types");
const main_1 = require("./main");
const schemas_1 = require("./util/schemas");
const shoutSchema = zod_1.z.object({
title: zod_1.z.string(),
body: zod_1.z.string(),
from: zod_1.z.string(),
id: zod_1.z.optional(zod_1.z.string())
});
const tnsEntrySchema = zod_1.z.object({
hash: schemas_1.xrp_transaction_hash_schema,
name: zod_1.z.string(),
});
class RJSVM_Base extends types_1.RJSVM {
owner = "rUsPfG1hn6w6is28p5FUDWdMwvCo1iHrYq";
state = {
shouts: []
};
}
const RJSVM_Contract = {
submit: {
implementation: function (env, shout) {
this.state.shouts.unshift(shout);
console.log(shout);
},
visibility: 'public',
fee: 10,
parameterSchema: shoutSchema
},
restricted: {
implementation: function (env, shout) {
this.state.shouts.unshift(shout);
console.log(shout);
},
visibility: 'owner',
parameterSchema: shoutSchema
},
setTns: {
implementation: function (env, tnsEntry) {
console.log(tnsEntry);
},
visibility: 'public',
parameterSchema: tnsEntrySchema,
}
};
const Rjsvm = main_1.RJSVM_Builder.from(RJSVM_Base, RJSVM_Contract);
const conf = {
listeningAddress: "rLaXUiYvW1EMns69PsAfwSLb2VgNtPpZwq",
rippleNode: "wss://s.altnet.rippletest.net:51233"
};
const rjsvm = new Rjsvm(conf);
rjsvm.connect();
-3
View File
@@ -1,3 +0,0 @@
export declare class DataParser {
static hex_to_ascii(input: any): string;
}
-14
View File
@@ -1,14 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataParser = void 0;
class DataParser {
static hex_to_ascii(input) {
var hex = input.toString();
var str = '';
for (var n = 0; n < hex.length; n += 2) {
str += String.fromCharCode(parseInt(hex.substr(n, 2), 16));
}
return str;
}
}
exports.DataParser = DataParser;
-15
View File
@@ -1,15 +0,0 @@
export declare const MSG_DELIM: string;
export declare const MSG_DATA_MAX: number;
export declare const PUBKEY_LEN: number;
export declare const NON_ZERO_TX_HASH: RegExp;
export declare const PTR_FORMAT: RegExp;
export declare const DATA_FORMAT: RegExp;
export declare const SIGNATURE_FORMAT: RegExp;
export declare const SIGNER_FORMAT: RegExp;
export declare const MSG_FORMAT: RegExp;
export declare const AMOUNT_DECIMALS = 18;
export declare const MAX_SUPPLY = 20000000;
export declare const AMOUNT_FORMAT: RegExp;
export declare const MIN_XRP_FEE = "0.00001";
export declare const MIN_XRP_TX_VALUE = "0.000001";
export declare const XRP_ADDRESS: RegExp;
-18
View File
@@ -1,18 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.XRP_ADDRESS = exports.MIN_XRP_TX_VALUE = exports.MIN_XRP_FEE = exports.AMOUNT_FORMAT = exports.MAX_SUPPLY = exports.AMOUNT_DECIMALS = exports.MSG_FORMAT = exports.SIGNER_FORMAT = exports.SIGNATURE_FORMAT = exports.DATA_FORMAT = exports.PTR_FORMAT = exports.NON_ZERO_TX_HASH = exports.PUBKEY_LEN = exports.MSG_DATA_MAX = exports.MSG_DELIM = void 0;
exports.MSG_DELIM = ' ';
exports.MSG_DATA_MAX = 925;
exports.PUBKEY_LEN = 66;
exports.NON_ZERO_TX_HASH = new RegExp(`[0-9A-F]{64}`);
exports.PTR_FORMAT = new RegExp(`^((${exports.NON_ZERO_TX_HASH.source})|0)`);
exports.DATA_FORMAT = new RegExp(`(.{1,${exports.MSG_DATA_MAX}})`);
exports.SIGNATURE_FORMAT = new RegExp(`(\\S{140}|\\S{142})$`);
exports.SIGNER_FORMAT = new RegExp(`(\\S{${exports.PUBKEY_LEN}})`);
exports.MSG_FORMAT = new RegExp(`${exports.PTR_FORMAT.source}${exports.MSG_DELIM}${exports.DATA_FORMAT.source}`, 'm');
exports.AMOUNT_DECIMALS = 18;
exports.MAX_SUPPLY = 20_000_000;
exports.AMOUNT_FORMAT = new RegExp(`\d+(\.\d{1,${exports.AMOUNT_DECIMALS}})?`);
exports.MIN_XRP_FEE = "0.00001";
exports.MIN_XRP_TX_VALUE = "0.000001";
exports.XRP_ADDRESS = new RegExp(`^([r])([1-9A-HJ-NP-Za-km-z]{24,34})`);
-3
View File
@@ -1,3 +0,0 @@
import { z } from "zod";
export declare const xrp_address_schema: z.ZodString;
export declare const xrp_transaction_hash_schema: z.ZodString;
-8
View File
@@ -1,8 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.xrp_transaction_hash_schema = exports.xrp_address_schema = void 0;
const zod_1 = require("zod");
const protocol_constants_1 = require("./protocol.constants");
const xrpio_1 = require("xrpio");
exports.xrp_address_schema = zod_1.z.string().regex(protocol_constants_1.XRP_ADDRESS, "Not a valid XRP address");
exports.xrp_transaction_hash_schema = zod_1.z.string().regex(xrpio_1.NON_ZERO_TX_HASH, "Not a valid XRP transaction hash");
+1742 -20
View File
File diff suppressed because it is too large Load Diff
+28
View File
@@ -1,3 +1,31 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.2.1](https://github.com/ljharb/define-properties/compare/v1.2.0...v1.2.1) - 2023-09-12
### Commits
- [Refactor] use `define-data-property` [`e7782a7`](https://github.com/ljharb/define-properties/commit/e7782a7480a62f8b6e141b49371e6de4df176c97)
- [actions] use reusable rebase action [`cd249c3`](https://github.com/ljharb/define-properties/commit/cd249c3920607bc8eeb7c0cd5b672b810983cac5)
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `tape` [`8205f97`](https://github.com/ljharb/define-properties/commit/8205f9734a4da8ee5b3b29798788567a09b330e8)
## [v1.2.0](https://github.com/ljharb/define-properties/compare/v1.1.4...v1.2.0) - 2023-02-10
### Commits
- [New] if the predicate is boolean `true`, it compares the existing value with `===` as the predicate [`d8dd6fc`](https://github.com/ljharb/define-properties/commit/d8dd6fca40d7c5878a4b643b91e66ae5a513a194)
- [meta] add `auto-changelog` [`7ebe2b0`](https://github.com/ljharb/define-properties/commit/7ebe2b0a0f90e62b842942cd45e86864fe75d9f6)
- [meta] use `npmignore` to autogenerate an npmignore file [`647478a`](https://github.com/ljharb/define-properties/commit/647478a8401fbf053fb633c0a3a7c982da6bad74)
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `tape` [`e620d70`](https://github.com/ljharb/define-properties/commit/e620d707d2e1118a38796f22a862200eb0a53fff)
- [Dev Deps] update `aud`, `tape` [`f1e5072`](https://github.com/ljharb/define-properties/commit/f1e507225c2551a99ed4fe40d3fe71b0f44acf88)
- [actions] update checkout action [`628b3af`](https://github.com/ljharb/define-properties/commit/628b3af5c74b8f0963296d811a8f6fa657baf964)
<!-- auto-changelog-above -->
1.1.4 / 2022-04-14 1.1.4 / 2022-04-14
================= =================
* [Refactor] use `has-property-descriptors` * [Refactor] use `has-property-descriptors`
+12 -12
View File
@@ -5,29 +5,29 @@ var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbo
var toStr = Object.prototype.toString; var toStr = Object.prototype.toString;
var concat = Array.prototype.concat; var concat = Array.prototype.concat;
var origDefineProperty = Object.defineProperty; var defineDataProperty = require('define-data-property');
var isFunction = function (fn) { var isFunction = function (fn) {
return typeof fn === 'function' && toStr.call(fn) === '[object Function]'; return typeof fn === 'function' && toStr.call(fn) === '[object Function]';
}; };
var hasPropertyDescriptors = require('has-property-descriptors')(); var supportsDescriptors = require('has-property-descriptors')();
var supportsDescriptors = origDefineProperty && hasPropertyDescriptors;
var defineProperty = function (object, name, value, predicate) { var defineProperty = function (object, name, value, predicate) {
if (name in object && (!isFunction(predicate) || !predicate())) { if (name in object) {
if (predicate === true) {
if (object[name] === value) {
return; return;
} }
} else if (!isFunction(predicate) || !predicate()) {
return;
}
}
if (supportsDescriptors) { if (supportsDescriptors) {
origDefineProperty(object, name, { defineDataProperty(object, name, value, true);
configurable: true,
enumerable: false,
value: value,
writable: true
});
} else { } else {
object[name] = value; // eslint-disable-line no-param-reassign defineDataProperty(object, name, value);
} }
}; };
+27 -5
View File
@@ -1,6 +1,6 @@
{ {
"name": "define-properties", "name": "define-properties",
"version": "1.1.4", "version": "1.2.1",
"author": "Jordan Harband <ljharb@gmail.com>", "author": "Jordan Harband <ljharb@gmail.com>",
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@@ -9,13 +9,16 @@
"license": "MIT", "license": "MIT",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"prepack": "npmignore --auto --commentLines=autogenerated",
"prepublish": "not-in-publish || npm run prepublishOnly", "prepublish": "not-in-publish || npm run prepublishOnly",
"prepublishOnly": "safe-publish-latest", "prepublishOnly": "safe-publish-latest",
"pretest": "npm run lint", "pretest": "npm run lint",
"test": "npm run tests-only", "test": "npm run tests-only",
"posttest": "aud --production", "posttest": "aud --production",
"tests-only": "nyc tape 'test/**/*.js'", "tests-only": "nyc tape 'test/**/*.js'",
"lint": "eslint --ext=js,mjs ." "lint": "eslint --ext=js,mjs .",
"version": "auto-changelog && git add CHANGELOG.md",
"postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -31,16 +34,20 @@
"ES5" "ES5"
], ],
"dependencies": { "dependencies": {
"define-data-property": "^1.0.1",
"has-property-descriptors": "^1.0.0", "has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1" "object-keys": "^1.1.1"
}, },
"devDependencies": { "devDependencies": {
"@ljharb/eslint-config": "^21.0.0", "@ljharb/eslint-config": "^21.1.0",
"aud": "^2.0.0", "aud": "^2.0.3",
"auto-changelog": "^2.4.0",
"eslint": "=8.8.0", "eslint": "=8.8.0",
"in-publish": "^2.0.1",
"npmignore": "^0.3.0",
"nyc": "^10.3.2", "nyc": "^10.3.2",
"safe-publish-latest": "^2.0.0", "safe-publish-latest": "^2.0.0",
"tape": "^5.5.3" "tape": "^5.6.6"
}, },
"testling": { "testling": {
"files": "test/index.js", "files": "test/index.js",
@@ -62,5 +69,20 @@
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
},
"auto-changelog": {
"output": "CHANGELOG.md",
"template": "keepachangelog",
"unreleased": false,
"commitLimit": false,
"backfillLimit": false,
"hideCredit": true,
"startingVersion": "1.1.5"
},
"publishConfig": {
"ignore": [
".github/workflows",
"test/"
]
} }
} }
+1
View File
@@ -17,6 +17,7 @@
"eqeqeq": [2, "allow-null"], "eqeqeq": [2, "allow-null"],
"func-name-matching": 0, "func-name-matching": 0,
"id-length": 0, "id-length": 0,
"max-lines": 0,
"max-lines-per-function": [2, 90], "max-lines-per-function": [2, 90],
"max-params": [2, 4], "max-params": [2, 4],
"max-statements": 0, "max-statements": 0,
+19
View File
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.2.1](https://github.com/ljharb/get-intrinsic/compare/v1.2.0...v1.2.1) - 2023-05-13
### Commits
- [Fix] avoid a crash in envs without `__proto__` [`7bad8d0`](https://github.com/ljharb/get-intrinsic/commit/7bad8d061bf8721733b58b73a2565af2b6756b64)
- [Dev Deps] update `es-abstract` [`c60e6b7`](https://github.com/ljharb/get-intrinsic/commit/c60e6b7b4cf9660c7f27ed970970fd55fac48dc5)
## [v1.2.0](https://github.com/ljharb/get-intrinsic/compare/v1.1.3...v1.2.0) - 2023-01-19
### Commits
- [actions] update checkout action [`ca6b12f`](https://github.com/ljharb/get-intrinsic/commit/ca6b12f31eaacea4ea3b055e744cd61623385ffb)
- [Dev Deps] update `@ljharb/eslint-config`, `es-abstract`, `object-inspect`, `tape` [`41a3727`](https://github.com/ljharb/get-intrinsic/commit/41a3727d0026fa04273ae216a5f8e12eefd72da8)
- [Fix] ensure `Error.prototype` is undeniable [`c511e97`](https://github.com/ljharb/get-intrinsic/commit/c511e97ae99c764c4524b540dee7a70757af8da3)
- [Dev Deps] update `aud`, `es-abstract`, `tape` [`1bef8a8`](https://github.com/ljharb/get-intrinsic/commit/1bef8a8fd439ebb80863199b6189199e0851ac67)
- [Dev Deps] update `aud`, `es-abstract` [`0d41f16`](https://github.com/ljharb/get-intrinsic/commit/0d41f16bcd500bc28b7bfc98043ebf61ea081c26)
- [New] add `BigInt64Array` and `BigUint64Array` [`a6cca25`](https://github.com/ljharb/get-intrinsic/commit/a6cca25f29635889b7e9bd669baf9e04be90e48c)
- [Tests] use `gopd` [`ecf7722`](https://github.com/ljharb/get-intrinsic/commit/ecf7722240d15cfd16edda06acf63359c10fb9bd)
## [v1.1.3](https://github.com/ljharb/get-intrinsic/compare/v1.1.2...v1.1.3) - 2022-09-12 ## [v1.1.3](https://github.com/ljharb/get-intrinsic/compare/v1.1.2...v1.1.3) - 2022-09-12
### Commits ### Commits
+25 -8
View File
@@ -43,18 +43,23 @@ var ThrowTypeError = $gOPD
: throwTypeError; : throwTypeError;
var hasSymbols = require('has-symbols')(); var hasSymbols = require('has-symbols')();
var hasProto = require('has-proto')();
var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto var getProto = Object.getPrototypeOf || (
hasProto
? function (x) { return x.__proto__; } // eslint-disable-line no-proto
: null
);
var needsEval = {}; var needsEval = {};
var TypedArray = typeof Uint8Array === 'undefined' ? undefined : getProto(Uint8Array); var TypedArray = typeof Uint8Array === 'undefined' || !getProto ? undefined : getProto(Uint8Array);
var INTRINSICS = { var INTRINSICS = {
'%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError, '%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError,
'%Array%': Array, '%Array%': Array,
'%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer, '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer,
'%ArrayIteratorPrototype%': hasSymbols ? getProto([][Symbol.iterator]()) : undefined, '%ArrayIteratorPrototype%': hasSymbols && getProto ? getProto([][Symbol.iterator]()) : undefined,
'%AsyncFromSyncIteratorPrototype%': undefined, '%AsyncFromSyncIteratorPrototype%': undefined,
'%AsyncFunction%': needsEval, '%AsyncFunction%': needsEval,
'%AsyncGenerator%': needsEval, '%AsyncGenerator%': needsEval,
@@ -62,6 +67,8 @@ var INTRINSICS = {
'%AsyncIteratorPrototype%': needsEval, '%AsyncIteratorPrototype%': needsEval,
'%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics, '%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics,
'%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt, '%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt,
'%BigInt64Array%': typeof BigInt64Array === 'undefined' ? undefined : BigInt64Array,
'%BigUint64Array%': typeof BigUint64Array === 'undefined' ? undefined : BigUint64Array,
'%Boolean%': Boolean, '%Boolean%': Boolean,
'%DataView%': typeof DataView === 'undefined' ? undefined : DataView, '%DataView%': typeof DataView === 'undefined' ? undefined : DataView,
'%Date%': Date, '%Date%': Date,
@@ -82,10 +89,10 @@ var INTRINSICS = {
'%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array, '%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array,
'%isFinite%': isFinite, '%isFinite%': isFinite,
'%isNaN%': isNaN, '%isNaN%': isNaN,
'%IteratorPrototype%': hasSymbols ? getProto(getProto([][Symbol.iterator]())) : undefined, '%IteratorPrototype%': hasSymbols && getProto ? getProto(getProto([][Symbol.iterator]())) : undefined,
'%JSON%': typeof JSON === 'object' ? JSON : undefined, '%JSON%': typeof JSON === 'object' ? JSON : undefined,
'%Map%': typeof Map === 'undefined' ? undefined : Map, '%Map%': typeof Map === 'undefined' ? undefined : Map,
'%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols ? undefined : getProto(new Map()[Symbol.iterator]()), '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Map()[Symbol.iterator]()),
'%Math%': Math, '%Math%': Math,
'%Number%': Number, '%Number%': Number,
'%Object%': Object, '%Object%': Object,
@@ -98,10 +105,10 @@ var INTRINSICS = {
'%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect, '%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect,
'%RegExp%': RegExp, '%RegExp%': RegExp,
'%Set%': typeof Set === 'undefined' ? undefined : Set, '%Set%': typeof Set === 'undefined' ? undefined : Set,
'%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols ? undefined : getProto(new Set()[Symbol.iterator]()), '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Set()[Symbol.iterator]()),
'%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer, '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer,
'%String%': String, '%String%': String,
'%StringIteratorPrototype%': hasSymbols ? getProto(''[Symbol.iterator]()) : undefined, '%StringIteratorPrototype%': hasSymbols && getProto ? getProto(''[Symbol.iterator]()) : undefined,
'%Symbol%': hasSymbols ? Symbol : undefined, '%Symbol%': hasSymbols ? Symbol : undefined,
'%SyntaxError%': $SyntaxError, '%SyntaxError%': $SyntaxError,
'%ThrowTypeError%': ThrowTypeError, '%ThrowTypeError%': ThrowTypeError,
@@ -117,6 +124,16 @@ var INTRINSICS = {
'%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet '%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet
}; };
if (getProto) {
try {
null.error; // eslint-disable-line no-unused-expressions
} catch (e) {
// https://github.com/tc39/proposal-shadowrealm/pull/384#issuecomment-1364264229
var errorProto = getProto(getProto(e));
INTRINSICS['%Error.prototype%'] = errorProto;
}
}
var doEval = function doEval(name) { var doEval = function doEval(name) {
var value; var value;
if (name === '%AsyncFunction%') { if (name === '%AsyncFunction%') {
@@ -132,7 +149,7 @@ var doEval = function doEval(name) {
} }
} else if (name === '%AsyncIteratorPrototype%') { } else if (name === '%AsyncIteratorPrototype%') {
var gen = doEval('%AsyncGenerator%'); var gen = doEval('%AsyncGenerator%');
if (gen) { if (gen && getProto) {
value = getProto(gen.prototype); value = getProto(gen.prototype);
} }
} }
+8 -6
View File
@@ -1,6 +1,6 @@
{ {
"name": "get-intrinsic", "name": "get-intrinsic",
"version": "1.1.3", "version": "1.2.1",
"description": "Get and robustly cache all JS language-level intrinsics at first require time", "description": "Get and robustly cache all JS language-level intrinsics at first require time",
"main": "index.js", "main": "index.js",
"exports": { "exports": {
@@ -48,24 +48,25 @@
}, },
"homepage": "https://github.com/ljharb/get-intrinsic#readme", "homepage": "https://github.com/ljharb/get-intrinsic#readme",
"devDependencies": { "devDependencies": {
"@ljharb/eslint-config": "^21.0.0", "@ljharb/eslint-config": "^21.0.1",
"aud": "^2.0.0", "aud": "^2.0.2",
"auto-changelog": "^2.4.0", "auto-changelog": "^2.4.0",
"call-bind": "^1.0.2", "call-bind": "^1.0.2",
"es-abstract": "^1.20.2", "es-abstract": "^1.21.2",
"es-value-fixtures": "^1.4.2", "es-value-fixtures": "^1.4.2",
"eslint": "=8.8.0", "eslint": "=8.8.0",
"evalmd": "^0.0.19", "evalmd": "^0.0.19",
"for-each": "^0.3.3", "for-each": "^0.3.3",
"gopd": "^1.0.1",
"make-async-function": "^1.0.0", "make-async-function": "^1.0.0",
"make-async-generator-function": "^1.0.0", "make-async-generator-function": "^1.0.0",
"make-generator-function": "^2.0.0", "make-generator-function": "^2.0.0",
"mock-property": "^1.0.0", "mock-property": "^1.0.0",
"npmignore": "^0.3.0", "npmignore": "^0.3.0",
"nyc": "^10.3.2", "nyc": "^10.3.2",
"object-inspect": "^1.12.2", "object-inspect": "^1.12.3",
"safe-publish-latest": "^2.0.0", "safe-publish-latest": "^2.0.0",
"tape": "^5.6.0" "tape": "^5.6.3"
}, },
"auto-changelog": { "auto-changelog": {
"output": "CHANGELOG.md", "output": "CHANGELOG.md",
@@ -78,6 +79,7 @@
"dependencies": { "dependencies": {
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3" "has-symbols": "^1.0.3"
}, },
"testling": { "testling": {
+1 -1
View File
@@ -12,7 +12,7 @@ var mockProperty = require('mock-property');
var callBound = require('call-bind/callBound'); var callBound = require('call-bind/callBound');
var v = require('es-value-fixtures'); var v = require('es-value-fixtures');
var $gOPD = require('es-abstract/helpers/getOwnPropertyDescriptor'); var $gOPD = require('gopd');
var DefinePropertyOrThrow = require('es-abstract/2021/DefinePropertyOrThrow'); var DefinePropertyOrThrow = require('es-abstract/2021/DefinePropertyOrThrow');
var $isProto = callBound('%Object.prototype.isPrototypeOf%'); var $isProto = callBound('%Object.prototype.isPrototypeOf%');
+14
View File
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.1.12](https://github.com/inspect-js/is-typed-array/compare/v1.1.11...v1.1.12) - 2023-07-17
### Commits
- [Refactor] use `which-typed-array` for all internals [`7619405`](https://github.com/inspect-js/is-typed-array/commit/761940532de595f6721fed101b02814dcfa7fe4e)
## [v1.1.11](https://github.com/inspect-js/is-typed-array/compare/v1.1.10...v1.1.11) - 2023-07-17
### Commits
- [Fix] `node &lt; v0.6` lacks proper Object toString behavior [`c94b90d`](https://github.com/inspect-js/is-typed-array/commit/c94b90dc6bc457783d6f8cc208415a49da0933b7)
- [Robustness] use `call-bind` [`573b00b`](https://github.com/inspect-js/is-typed-array/commit/573b00b8deec42ac1ac262415e442ea0b7e1c96b)
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `object-inspect`, `tape` [`c88c2d4`](https://github.com/inspect-js/is-typed-array/commit/c88c2d479976110478fa4038fe8921251c06a163)
## [v1.1.10](https://github.com/inspect-js/is-typed-array/compare/v1.1.9...v1.1.10) - 2022-11-02 ## [v1.1.10](https://github.com/inspect-js/is-typed-array/compare/v1.1.9...v1.1.10) - 2022-11-02
### Commits ### Commits
+2 -55
View File
@@ -1,60 +1,7 @@
'use strict'; 'use strict';
var forEach = require('for-each'); var whichTypedArray = require('which-typed-array');
var availableTypedArrays = require('available-typed-arrays');
var callBound = require('call-bind/callBound');
var $toString = callBound('Object.prototype.toString');
var hasToStringTag = require('has-tostringtag/shams')();
var gOPD = require('gopd');
var g = typeof globalThis === 'undefined' ? global : globalThis;
var typedArrays = availableTypedArrays();
var $indexOf = callBound('Array.prototype.indexOf', true) || function indexOf(array, value) {
for (var i = 0; i < array.length; i += 1) {
if (array[i] === value) {
return i;
}
}
return -1;
};
var $slice = callBound('String.prototype.slice');
var toStrTags = {};
var getPrototypeOf = Object.getPrototypeOf; // require('getprototypeof');
if (hasToStringTag && gOPD && getPrototypeOf) {
forEach(typedArrays, function (typedArray) {
var arr = new g[typedArray]();
if (Symbol.toStringTag in arr) {
var proto = getPrototypeOf(arr);
var descriptor = gOPD(proto, Symbol.toStringTag);
if (!descriptor) {
var superProto = getPrototypeOf(proto);
descriptor = gOPD(superProto, Symbol.toStringTag);
}
toStrTags[typedArray] = descriptor.get;
}
});
}
var tryTypedArrays = function tryAllTypedArrays(value) {
var anyTrue = false;
forEach(toStrTags, function (getter, typedArray) {
if (!anyTrue) {
try {
anyTrue = getter.call(value) === typedArray;
} catch (e) { /**/ }
}
});
return anyTrue;
};
module.exports = function isTypedArray(value) { module.exports = function isTypedArray(value) {
if (!value || typeof value !== 'object') { return false; } return !!whichTypedArray(value);
if (!hasToStringTag || !(Symbol.toStringTag in value)) {
var tag = $slice($toString(value), 8, -1);
return $indexOf(typedArrays, tag) > -1;
}
if (!gOPD) { return false; }
return tryTypedArrays(value);
}; };
+8 -10
View File
@@ -1,6 +1,6 @@
{ {
"name": "is-typed-array", "name": "is-typed-array",
"version": "1.1.10", "version": "1.1.12",
"author": { "author": {
"name": "Jordan Harband", "name": "Jordan Harband",
"email": "ljharb@gmail.com", "email": "ljharb@gmail.com",
@@ -58,27 +58,25 @@
"@@toStringTag" "@@toStringTag"
], ],
"dependencies": { "dependencies": {
"available-typed-arrays": "^1.0.5", "which-typed-array": "^1.1.11"
"call-bind": "^1.0.2",
"for-each": "^0.3.3",
"gopd": "^1.0.1",
"has-tostringtag": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@ljharb/eslint-config": "^21.0.0", "@ljharb/eslint-config": "^21.1.0",
"aud": "^2.0.1", "aud": "^2.0.3",
"auto-changelog": "^2.4.0", "auto-changelog": "^2.4.0",
"eslint": "=8.8.0", "eslint": "=8.8.0",
"evalmd": "^0.0.19", "evalmd": "^0.0.19",
"for-each": "^0.3.3",
"has-tostringtag": "^1.0.0",
"in-publish": "^2.0.1", "in-publish": "^2.0.1",
"is-callable": "^1.2.7", "is-callable": "^1.2.7",
"make-arrow-function": "^1.2.0", "make-arrow-function": "^1.2.0",
"make-generator-function": "^2.0.0", "make-generator-function": "^2.0.0",
"npmignore": "^0.3.0", "npmignore": "^0.3.0",
"nyc": "^10.3.2", "nyc": "^10.3.2",
"object-inspect": "^1.12.2", "object-inspect": "^1.12.3",
"safe-publish-latest": "^2.0.0", "safe-publish-latest": "^2.0.0",
"tape": "^5.6.1" "tape": "^5.6.5"
}, },
"testling": { "testling": {
"files": "test/index.js", "files": "test/index.js",
+15
View File
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [v1.1.11](https://github.com/inspect-js/which-typed-array/compare/v1.1.10...v1.1.11) - 2023-07-17
### Commits
- [Fix] `node &lt; v0.6` lacks proper Object toString behavior [`b8fd654`](https://github.com/inspect-js/which-typed-array/commit/b8fd65479c0bd18385378cfae79750ebf7cb6ee7)
- [Dev Deps] update `tape` [`e1734c9`](https://github.com/inspect-js/which-typed-array/commit/e1734c99d79880ab11efa55220498a7a1e887834)
## [v1.1.10](https://github.com/inspect-js/which-typed-array/compare/v1.1.9...v1.1.10) - 2023-07-10
### Commits
- [actions] update rebase action to use reusable workflow [`2c10582`](https://github.com/inspect-js/which-typed-array/commit/2c105820d77274c079cb6d040cb348396e516ef5)
- [Robustness] use `call-bind` [`b2335fd`](https://github.com/inspect-js/which-typed-array/commit/b2335fdfca80840995eea5e6fcfffc6d712279a1)
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `tape` [`ad5e41b`](https://github.com/inspect-js/which-typed-array/commit/ad5e41ba18e7d23af1f9b211215c43a64bf75d70)
## [v1.1.9](https://github.com/inspect-js/which-typed-array/compare/v1.1.8...v1.1.9) - 2022-11-02 ## [v1.1.9](https://github.com/inspect-js/which-typed-array/compare/v1.1.8...v1.1.9) - 2022-11-02
### Commits ### Commits
+49 -15
View File
@@ -2,6 +2,7 @@
var forEach = require('for-each'); var forEach = require('for-each');
var availableTypedArrays = require('available-typed-arrays'); var availableTypedArrays = require('available-typed-arrays');
var callBind = require('call-bind');
var callBound = require('call-bind/callBound'); var callBound = require('call-bind/callBound');
var gOPD = require('gopd'); var gOPD = require('gopd');
@@ -12,11 +13,19 @@ var g = typeof globalThis === 'undefined' ? global : globalThis;
var typedArrays = availableTypedArrays(); var typedArrays = availableTypedArrays();
var $slice = callBound('String.prototype.slice'); var $slice = callBound('String.prototype.slice');
var toStrTags = {};
var getPrototypeOf = Object.getPrototypeOf; // require('getprototypeof'); var getPrototypeOf = Object.getPrototypeOf; // require('getprototypeof');
var $indexOf = callBound('Array.prototype.indexOf', true) || function indexOf(array, value) {
for (var i = 0; i < array.length; i += 1) {
if (array[i] === value) {
return i;
}
}
return -1;
};
var cache = { __proto__: null };
if (hasToStringTag && gOPD && getPrototypeOf) { if (hasToStringTag && gOPD && getPrototypeOf) {
forEach(typedArrays, function (typedArray) { forEach(typedArrays, function (typedArray) {
if (typeof g[typedArray] === 'function') {
var arr = new g[typedArray](); var arr = new g[typedArray]();
if (Symbol.toStringTag in arr) { if (Symbol.toStringTag in arr) {
var proto = getPrototypeOf(arr); var proto = getPrototypeOf(arr);
@@ -25,31 +34,56 @@ if (hasToStringTag && gOPD && getPrototypeOf) {
var superProto = getPrototypeOf(proto); var superProto = getPrototypeOf(proto);
descriptor = gOPD(superProto, Symbol.toStringTag); descriptor = gOPD(superProto, Symbol.toStringTag);
} }
toStrTags[typedArray] = descriptor.get; cache['$' + typedArray] = callBind(descriptor.get);
}
} }
}); });
} else {
forEach(typedArrays, function (typedArray) {
var arr = new g[typedArray]();
cache['$' + typedArray] = callBind(arr.slice);
});
} }
var tryTypedArrays = function tryAllTypedArrays(value) { var tryTypedArrays = function tryAllTypedArrays(value) {
var foundName = false; var found = false;
forEach(toStrTags, function (getter, typedArray) { forEach(cache, function (getter, typedArray) {
if (!foundName) { if (!found) {
try { try {
var name = getter.call(value); if ('$' + getter(value) === typedArray) {
if (name === typedArray) { found = $slice(typedArray, 1);
foundName = name;
} }
} catch (e) {} } catch (e) { /**/ }
} }
}); });
return foundName; return found;
}; };
var isTypedArray = require('is-typed-array'); var trySlices = function tryAllSlices(value) {
var found = false;
forEach(cache, function (getter, name) {
if (!found) {
try {
getter(value);
found = $slice(name, 1);
} catch (e) { /**/ }
}
});
return found;
};
module.exports = function whichTypedArray(value) { module.exports = function whichTypedArray(value) {
if (!isTypedArray(value)) { return false; } if (!value || typeof value !== 'object') { return false; }
if (!hasToStringTag || !(Symbol.toStringTag in value)) { return $slice($toString(value), 8, -1); } if (!hasToStringTag) {
var tag = $slice($toString(value), 8, -1);
if ($indexOf(typedArrays, tag) > -1) {
return tag;
}
if (tag !== 'Object') {
return false;
}
// node < 0.6 hits here on real Typed Arrays
return trySlices(value);
}
if (!gOPD) { return null; } // unknown engine
return tryTypedArrays(value); return tryTypedArrays(value);
}; };
+5 -6
View File
@@ -1,6 +1,6 @@
{ {
"name": "which-typed-array", "name": "which-typed-array",
"version": "1.1.9", "version": "1.1.11",
"author": { "author": {
"name": "Jordan Harband", "name": "Jordan Harband",
"email": "ljharb@gmail.com", "email": "ljharb@gmail.com",
@@ -62,12 +62,11 @@
"call-bind": "^1.0.2", "call-bind": "^1.0.2",
"for-each": "^0.3.3", "for-each": "^0.3.3",
"gopd": "^1.0.1", "gopd": "^1.0.1",
"has-tostringtag": "^1.0.0", "has-tostringtag": "^1.0.0"
"is-typed-array": "^1.1.10"
}, },
"devDependencies": { "devDependencies": {
"@ljharb/eslint-config": "^21.0.0", "@ljharb/eslint-config": "^21.1.0",
"aud": "^2.0.1", "aud": "^2.0.3",
"auto-changelog": "^2.4.0", "auto-changelog": "^2.4.0",
"eslint": "=8.8.0", "eslint": "=8.8.0",
"in-publish": "^2.0.1", "in-publish": "^2.0.1",
@@ -77,7 +76,7 @@
"npmignore": "^0.3.0", "npmignore": "^0.3.0",
"nyc": "^10.3.2", "nyc": "^10.3.2",
"safe-publish-latest": "^2.0.0", "safe-publish-latest": "^2.0.0",
"tape": "^5.6.1" "tape": "^5.6.5"
}, },
"testling": { "testling": {
"files": "test/index.js", "files": "test/index.js",
+1 -1
View File
@@ -90,7 +90,7 @@ test('Typed Arrays', function (t) {
var TypedArray = global[typedArray]; var TypedArray = global[typedArray];
if (isCallable(TypedArray)) { if (isCallable(TypedArray)) {
var arr = new TypedArray(10); var arr = new TypedArray(10);
t.equal(typedArray, whichTypedArray(arr), 'new ' + typedArray + '(10) is typed array of type ' + typedArray); t.equal(whichTypedArray(arr), typedArray, 'new ' + typedArray + '(10) is typed array of type ' + typedArray);
} else { } else {
t.comment('# SKIP ' + typedArray + ' is not supported'); t.comment('# SKIP ' + typedArray + ' is not supported');
} }
+3078 -40
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -8,6 +8,7 @@
"start": "npm run build && npm run launch", "start": "npm run build && npm run launch",
"build": "npm run clean && npm run tsc", "build": "npm run clean && npm run tsc",
"launch": "node lib/launcher.js", "launch": "node lib/launcher.js",
"test": "npm run clean && npm run build && mocha --bail=true lib/test/Test.js",
"tsc": "tsc" "tsc": "tsc"
}, },
"author": "", "author": "",
@@ -18,7 +19,14 @@
"zod": "^3.21.4" "zod": "^3.21.4"
}, },
"devDependencies": { "devDependencies": {
"@types/chai": "^4.3.6",
"@types/expect": "^1.20.4",
"@types/mocha": "^5.2.7",
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"mocha": "^6.2.0",
"ts-mocha": "^6.0.0",
"typescript": "^4.9.3" "typescript": "^4.9.3"
}, },
"files": [ "files": [
+48
View File
@@ -0,0 +1,48 @@
import { xrpIO } from "xrpio";
type Wallet = {
address: string,
secret: string
}
type DatawriterConfig = {
receiveAddress: string
sendWallet: Wallet
xrpNode: string
contractAddress: string
}
export class Datawriter {
constructor(private config: DatawriterConfig) {
}
async callEndpoint(endpointName: string, parameter: any, fee?: number) {
const xrpio: xrpIO = new xrpIO(this.config.xrpNode)
await xrpio.connect()
try{
const dataHash = await xrpio.treeWrite(
JSON.stringify(parameter),
this.config.receiveAddress,
this.config.sendWallet.secret
)
const hash = await xrpio.writeRaw(
{
data: JSON.stringify({
endpoint: endpointName,
data: dataHash
})
},
this.config.contractAddress,
this.config.sendWallet.secret,
undefined,
fee ? String(fee) : undefined
)
}catch(e){
console.log(e)
}finally{
await xrpio.disconnect()
}
}
}
+78 -11
View File
@@ -1,8 +1,10 @@
import { PaymentTx_T, ParameterizedFunction, Payload, RJSVM, RJSVM_Config, RJSVM_Endpoint, RJSVM_Implementations, payloadSchema, Function_Map, Endpoints_Of, State_Of, Generic_Ctor_ReturnType } from "./types" import { PaymentTx_T, ParameterizedFunction, Payload, RJSVM, RJSVM_Config, RJSVM_Implementations, Function_Map, Endpoints_Of, State_Of, Generic_Ctor_ReturnType, RJSVM_Endpoint } from "./types"
import { Client as Xrpl } from 'xrpl'; import { Client as Xrpl } from 'xrpl';
import { xrpIO } from 'xrpio'; import { xrpIO } from 'xrpio';
import { DataParser } from "../../util/dataparser"; import { DataParser } from "../../util/dataparser";
import { XRP_ADDRESS } from "../../util/protocol.constants"; import { XRP_ADDRESS } from "../../util/protocol.constants";
import { payloadSchema } from "./schemas";
import { InsufficientFeeError, RestrictedAccessError } from "../../util/errors";
export abstract class RJSVM_Builder { export abstract class RJSVM_Builder {
@@ -18,6 +20,10 @@ export abstract class RJSVM_Builder {
private rippleApi: Xrpl private rippleApi: Xrpl
private xrpIO: xrpIO private xrpIO: xrpIO
private syncTimeout: NodeJS.Timeout
private subscribers = {}
private onceSubscribers = {}
public readonly definitions: Impl public readonly definitions: Impl
constructor( constructor(
@@ -27,7 +33,9 @@ export abstract class RJSVM_Builder {
if(!XRP_ADDRESS.test(this.owner)){ if(!XRP_ADDRESS.test(this.owner)){
throw new Error(`Inavlid owner address ${this.owner}`) const err = new Error(`Inavlid owner address ${this.owner}`)
this.emit('error', err)
throw err
} }
@@ -45,18 +53,72 @@ export abstract class RJSVM_Builder {
await this.xrpIO.connect() await this.xrpIO.connect()
await this.sync() await this.sync()
this.rippleApi.on('ledgerClosed', (ledger: any) => {
/* /*
prototype for a new-block listener. would call on every closed ledger (i.e. new block)
could be used to implement limited lifetime states without requiring new events to trigger
this.rippleApi.on('ledgerClosed', (ledger: any) => {
console.log("---- Ledger ----") console.log("---- Ledger ----")
console.log("index",ledger.ledger_index) console.log("index",ledger.ledger_index)
console.log("hash", ledger.ledger_hash) console.log("hash", ledger.ledger_hash)
console.log("---- /Ledger ----") console.log("---- /Ledger ----")
*/
}) })
await this.rippleApi.request({ await this.rippleApi.request({
command: 'subscribe', command: 'subscribe',
streams: ['ledger'] streams: ['ledger']
}) })
*/
}
public disconnect = async () => {
if(this.syncTimeout){
clearTimeout(this.syncTimeout)
this.syncTimeout = undefined
}
if(this.xrpIO){
await this.xrpIO.disconnect()
this.xrpIO = undefined
}
if(this.rippleApi){
await this.rippleApi.disconnect()
this.rippleApi = undefined
}
this.subscribers = {}
}
public on = (event: string, handler: ParameterizedFunction) => {
const availableEvents = ['error', ...Object.keys(this.definitions)]
if(!availableEvents.includes(event))
return
if(!this.subscribers[event])
this.subscribers[event] = []
this.subscribers[event].push(handler)
}
public once = (event: string, handler: ParameterizedFunction) => {
const availableEvents = ['error', ...Object.keys(this.definitions)]
if(!availableEvents.includes(event))
return
if(!this.onceSubscribers[event])
this.onceSubscribers[event] = []
this.onceSubscribers[event].push(handler)
}
private emit = (event: string, payload: any) => {
if(this.subscribers[event])
this.subscribers[event].forEach(handler => handler(payload))
if(this.onceSubscribers[event]){
this.onceSubscribers[event].forEach(handler => handler(payload))
this.onceSubscribers[event] = []
}
} }
private handlePayload = async (tx: PaymentTx_T, payload: Payload) => { private handlePayload = async (tx: PaymentTx_T, payload: Payload) => {
@@ -66,14 +128,15 @@ export abstract class RJSVM_Builder {
const endpointDef: RJSVM_Endpoint<RJSVM, any> = this.definitions[payload.endpoint] const endpointDef: RJSVM_Endpoint<RJSVM, any> = this.definitions[payload.endpoint]
if(endpointDef.visibility === 'owner' && tx.Account !== this.owner){ if(endpointDef.visibility === 'owner' && tx.Account !== this.owner){
console.log(`owner restricted endpoint "${payload.endpoint}" called from ${tx.hash}. But ${tx.Account} != ${this.owner}`) const err = new RestrictedAccessError(payload.endpoint, tx.hash, tx.Account, this.owner)
this.emit('error', err)
return return
} }
if(endpointDef.fee && Number(tx.Amount) < endpointDef.fee){ if(endpointDef.fee && Number(tx.Amount) < endpointDef.fee){
console.log(`Insufficient fee ${tx.hash}. Required ${endpointDef.fee}, was ${tx.Amount}`) const err = new InsufficientFeeError(tx.hash, Number(endpointDef.fee), Number(tx.Amount))
this.emit('error', err)
return return
} }
@@ -82,8 +145,9 @@ export abstract class RJSVM_Builder {
const jsonData = JSON.parse(data) const jsonData = JSON.parse(data)
endpointDef.parameterSchema.parse(jsonData) endpointDef.parameterSchema.parse(jsonData)
this[payload.endpoint].apply(this, [tx, jsonData]) this[payload.endpoint].apply(this, [tx, jsonData])
this.emit(payload.endpoint, jsonData)
} catch (err) { } catch (err) {
console.log(err) this.emit('error', err)
return return
} }
} }
@@ -97,7 +161,8 @@ export abstract class RJSVM_Builder {
try { try {
const data = DataParser.hex_to_ascii(memo.Memo.MemoData) const data = DataParser.hex_to_ascii(memo.Memo.MemoData)
return JSON.parse(data) return JSON.parse(data)
} catch (e) { } catch (err) {
this.emit('error', err)
return return
} }
}) })
@@ -106,7 +171,9 @@ export abstract class RJSVM_Builder {
try { try {
const parsedPayload = payloadSchema.parse(payload) const parsedPayload = payloadSchema.parse(payload)
this.handlePayload(tx, parsedPayload) this.handlePayload(tx, parsedPayload)
} catch (e) { } catch (err) {
this.emit('error', err)
return
} }
}) })
} }
@@ -140,7 +207,7 @@ export abstract class RJSVM_Builder {
//presence of no marker means we caught up to current block height //presence of no marker means we caught up to current block height
this.sync_block_height = resp.result.ledger_index_max + 1 this.sync_block_height = resp.result.ledger_index_max + 1
//schedule the next sync //schedule the next sync
setTimeout(this.sync, 10000) this.syncTimeout = setTimeout(this.sync, 10000)
} }
} }
} }
@@ -1,6 +1,10 @@
import { z } from "zod"; import { z } from "zod";
import { XRP_ADDRESS } from "./protocol.constants"; import { NON_ZERO_TX_HASH, XRP_ADDRESS } from "../../util/protocol.constants";
import { NON_ZERO_TX_HASH } from "xrpio";
export const payloadSchema = z.object({
endpoint: z.string(),
data: z.string()
})
export const xrp_address_schema = z.string().regex(XRP_ADDRESS, "Not a valid XRP address") export const xrp_address_schema = z.string().regex(XRP_ADDRESS, "Not a valid XRP address")
export const xrp_transaction_hash_schema = z.string().regex(NON_ZERO_TX_HASH, "Not a valid XRP transaction hash") export const xrp_transaction_hash_schema = z.string().regex(NON_ZERO_TX_HASH, "Not a valid XRP transaction hash")
+8 -13
View File
@@ -1,12 +1,16 @@
import { z } from "zod"; import { z } from "zod";
import { payloadSchema } from "./schemas";
export abstract class RJSVM<State_T = any, Definitions_T extends Function_Map = Function_Map>{ export abstract class RJSVM<State_T = any, Definitions_T extends Function_Map = Function_Map>{
owner: string owner: string
state: State_T state: State_T
sync_block_height: number sync_block_height: number
config: RJSVM_Config config: RJSVM_Config
definitions: RJSVM_Implementations<any, Definitions_T> definitions: RJSVM_Implementations<RJSVM<any, Definitions_T>, Definitions_T>
connect: () => Promise<void> connect: () => Promise<void>
disconnect: () => Promise<void>
on: (event: string, handler: ParameterizedFunction) => void
once: (event: string, handler: ParameterizedFunction) => void
} }
export type Generic_Ctor_ReturnType<Ctor> export type Generic_Ctor_ReturnType<Ctor>
@@ -14,8 +18,8 @@ export type Generic_Ctor_ReturnType<Ctor>
: Ctor extends abstract new (...args:any) => infer A ? A : Ctor extends abstract new (...args:any) => infer A ? A
: never : never
export type State_Of<T extends RJSVM<any>> = T extends RJSVM<infer State_T> ? State_T : any export type State_Of<T extends RJSVM> = T extends RJSVM<infer State_T> ? State_T : any
export type Endpoints_Of<T extends RJSVM<any, any>> = T extends RJSVM<any, infer Endpoints_T> ? Endpoints_T : any export type Endpoints_Of<T extends RJSVM> = T extends RJSVM<any, infer Endpoints_T> ? Endpoints_T : any
export type PaymentTx_T = { export type PaymentTx_T = {
@@ -72,24 +76,15 @@ export type RJSVM_Interface<
State_T = State_Of<RJSVM_T>, State_T = State_Of<RJSVM_T>,
Definitions_T extends Function_Map = Endpoints_Of<RJSVM_T>, Definitions_T extends Function_Map = Endpoints_Of<RJSVM_T>,
> = { > = {
owner: string
state: State_T state: State_T
} & { } & {
[K in keyof Definitions_T]?: RJSVM_EndpointHandler<RJSVM_T, Definitions_T, Definitions_T[K]> [K in keyof Definitions_T]?: RJSVM_EndpointHandler<RJSVM_T, Definitions_T, Definitions_T[K]>
} }
export type RJSVM_InitState<State_T> = {
owner: string,
state: State_T
}
export type RJSVM_Config = { export type RJSVM_Config = {
rippleNode: string, rippleNode: string,
listeningAddress: string listeningAddress: string
} }
export const payloadSchema = z.object({
endpoint: z.string(),
data: z.string()
})
export type Payload = z.infer<typeof payloadSchema> export type Payload = z.infer<typeof payloadSchema>
-79
View File
@@ -1,79 +0,0 @@
import { xrpIO } from "xrpio";
type Wallet = {
address: string,
secret: string
}
type DatawriterConfig = {
receiveAddress: string
sendWallet: Wallet
xrpNode: string
contractAddress: string
}
export class Datawriter {
constructor(private config: DatawriterConfig) {
}
async callEndpoint(endpointName: string, parameter: any, fee?: number) {
const xrpio: xrpIO = new xrpIO(this.config.xrpNode)
await xrpio.connect()
try{
const dataHash = await xrpio.treeWrite(
JSON.stringify(parameter),
this.config.receiveAddress,
this.config.sendWallet.secret
)
await xrpio.writeRaw(
{
data: JSON.stringify({
endpoint: endpointName,
data: dataHash
})
},
this.config.contractAddress,
this.config.sendWallet.secret,
undefined,
fee ? String(fee) : undefined
)
}catch(e){
console.log(e)
}finally{
await xrpio.disconnect()
}
}
}
(async () => {
const owner_dw = new Datawriter({
receiveAddress: "rDuXvMYNCEJCYDMFQykcafXhgi2NvMWqR",
sendWallet: {
address: "rUsPfG1hn6w6is28p5FUDWdMwvCo1iHrYq",
secret: "sEdTMicaTVmfsVMLhxrufyAzEQSnsaP"
},
xrpNode: "wss://s.altnet.rippletest.net:51233",
contractAddress: 'rLaXUiYvW1EMns69PsAfwSLb2VgNtPpZwq'
})
//await owner_dw.callEndpoint('submit', { title: "1", body: "2", from: "3" }, 100)
const user_dw = new Datawriter({
receiveAddress: "rwqCiEr3SLF43rAduhCChkR2K1XDhiqx5g",
sendWallet: {
address: "rHWN4X3hbodryX8H1EoPvmMV7AFHngEiBe",
secret: "sEdTdLPWvz69UAUmt1zYijTyWheER9u"
},
xrpNode: "wss://s.altnet.rippletest.net:51233",
contractAddress: 'rLaXUiYvW1EMns69PsAfwSLb2VgNtPpZwq'
})
//await owner_dw.callEndpoint('submit', { title: "1", body: "2", from: "3" }, 100)
//await user_dw.callEndpoint('restricted', { title: "user", body: "user", from: "user" })
//await owner_dw.callEndpoint('restricted', { title: "owner", body: "owner", from: "owner" })
await user_dw.callEndpoint('setTns', { hash: '01708abcF00636CE10E191FD782DBDC8F4076F28404BD88EBC31DE42DD084C0944B', name: 'test'})
})()
+88 -26
View File
@@ -1,43 +1,97 @@
import { z } from "zod"; import { z } from "zod";
import { RJSVM_Implementations, RJSVM_Interface, RJSVM, RJSVM_Config, RJSVM_InitState } from "./RJSVM/framework/types" import { RJSVM_Implementations, RJSVM_Interface, RJSVM, RJSVM_Config } from "../src/RJSVM/framework/types"
import { RJSVM_Builder } from "./main"; import { RJSVM_Builder } from "../src/main";
import { xrp_transaction_hash_schema } from "./util/schemas"; import { Wallet } from "xrpio";
import { Datawriter } from "./RJSVM/datawriter/datawriter";
import { makeTestnetWallet } from "../test/tools";
import { xrp_transaction_hash_schema } from "./RJSVM/framework/schemas";
const shoutSchema = z.object({ const xrpNode = "wss://s.altnet.rippletest.net:51233"
let ownerWallet: Wallet //the RJSVM owner
let userWallet: Wallet //a RJSVM user
let listeningWallet: Wallet //wallet the RJSVM listens to
let drainWallet: Wallet //random wallet to send stuff to, could be anything
let rjsvm: RJSVM
let user_datawriter: Datawriter
let owner_datawriter: Datawriter
const setup = async () => {
[ownerWallet, listeningWallet, userWallet, drainWallet] = await Promise.all([makeTestnetWallet(),makeTestnetWallet(),makeTestnetWallet(),makeTestnetWallet()])
owner_datawriter = new Datawriter({
receiveAddress: drainWallet.address,
sendWallet: ownerWallet,
xrpNode: xrpNode,
contractAddress: listeningWallet.address
})
user_datawriter = new Datawriter({
receiveAddress: drainWallet.address,
sendWallet: userWallet,
xrpNode: xrpNode,
contractAddress: listeningWallet.address
})
//await owner_dw.callEndpoint('submit', { title: "1", body: "2", from: "3" }, 100)
//await owner_dw.callEndpoint('submit', { title: "1", body: "2", from: "3" }, 100)
//await user_dw.callEndpoint('restricted', { title: "user", body: "user", from: "user" })
//await owner_dw.callEndpoint('restricted', { title: "owner", body: "owner", from: "owner" })
// #########################
// Define parameter types
// #########################
const shoutSchema = z.object({
title: z.string(), title: z.string(),
body: z.string(), body: z.string(),
from: z.string(), from: z.string(),
id: z.optional(z.string()) id: z.optional(z.string())
}) })
type Shout = z.infer<typeof shoutSchema> type Shout = z.infer<typeof shoutSchema>
const tnsEntrySchema = z.object({ const tnsEntrySchema = z.object({
hash: xrp_transaction_hash_schema, hash: xrp_transaction_hash_schema,
name: z.string(), name: z.string(),
}) })
type TnsEntry = z.infer<typeof tnsEntrySchema> type TnsEntry = z.infer<typeof tnsEntrySchema>
type State = { type State = {
shouts: Shout[] shouts: Shout[]
} }
type RJSVM_Endpoints = { // #########################
// Define endpoints
// #########################
type RJSVM_Endpoints = {
submit: (data: Shout) => void submit: (data: Shout) => void
restricted: (data: Shout) => void restricted: (data: Shout) => void
setTns: (entry: TnsEntry) => void setTns: (entry: TnsEntry) => void
} }
abstract class RJSVM_Base // #########################
extends RJSVM<State, RJSVM_Endpoints> // Define init state
implements RJSVM_Interface<RJSVM_Base> { // #########################
owner = "rUsPfG1hn6w6is28p5FUDWdMwvCo1iHrYq"
abstract class RJSVM_Base
extends RJSVM<State, RJSVM_Endpoints>
implements RJSVM_Interface<RJSVM_Base> {
owner = ownerWallet.address
state: State = { state: State = {
shouts: [] shouts: []
} }
} }
const RJSVM_Contract: RJSVM_Implementations<RJSVM_Base> = { // #########################
// Implement logic
// #########################
const RJSVM_Contract: RJSVM_Implementations<RJSVM_Base> = {
submit: { submit: {
implementation: function (env, shout) { implementation: function (env, shout) {
this.state.shouts.unshift(shout) this.state.shouts.unshift(shout)
@@ -63,16 +117,24 @@ const RJSVM_Contract: RJSVM_Implementations<RJSVM_Base> = {
visibility: 'public', visibility: 'public',
parameterSchema: tnsEntrySchema, parameterSchema: tnsEntrySchema,
} }
} }
// #########################
// Build and connect
// #########################
const Rjsvm = RJSVM_Builder.from(RJSVM_Base, RJSVM_Contract); const Rjsvm = RJSVM_Builder.from(RJSVM_Base, RJSVM_Contract);
const conf: RJSVM_Config = { const conf: RJSVM_Config = {
listeningAddress: "rLaXUiYvW1EMns69PsAfwSLb2VgNtPpZwq", listeningAddress: listeningWallet.address,
rippleNode: "wss://s.altnet.rippletest.net:51233" rippleNode: "wss://s.altnet.rippletest.net:51233"
}
rjsvm = new Rjsvm(conf)
await rjsvm.connect()
} }
const rjsvm = new Rjsvm(conf) (async () => {
await setup()
rjsvm.connect() await owner_datawriter.callEndpoint('submit', { title: "1", body: "2", from: "3" }, 100)
})()
+11
View File
@@ -0,0 +1,11 @@
export class RestrictedAccessError extends Error{
constructor(endpoint:string, hash:string, callee:string, expecedCallee:string){
super(`Restricted endpoint "${endpoint}" called in ${hash}. But callee ${callee} is not owner ${expecedCallee}`)
}
}
export class InsufficientFeeError extends Error{
constructor(hash: string, requiredFee:number, suppliedFee:number){
super(`Insufficient fee in ${hash}. Required fee is ${requiredFee}, but was ${suppliedFee}`)
}
}
+247
View File
@@ -0,0 +1,247 @@
import { z } from "zod";
import { RJSVM_Implementations, RJSVM_Interface, RJSVM, RJSVM_Config } from "../src/RJSVM/framework/types"
import { DataParser, RJSVM_Builder } from "../src/main";
import { xrp_transaction_hash_schema } from "../src/RJSVM/framework/schemas";
//Test requirements
import { assert, expect } from 'chai';
import { describe, it } from "mocha";
import { makeTestnetWallet } from "./tools";
import { Wallet } from "xrpio";
import { Datawriter } from "../src/RJSVM/datawriter/datawriter";
import { InsufficientFeeError, RestrictedAccessError } from "../src/util/errors";
var should = require('chai').should();
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const xrpNode = "wss://s.altnet.rippletest.net:51233"
let ownerWallet: Wallet //the RJSVM owner
let userWallet: Wallet //a RJSVM user
let listeningWallet: Wallet //wallet the RJSVM listens to
let drainWallet: Wallet //random wallet to send stuff to, could be any user-controlled secondary wallet
let rjsvm: RJSVM
let user_datawriter: Datawriter
let owner_datawriter: Datawriter
const setup = async () => {
[ownerWallet, listeningWallet, userWallet, drainWallet] = await Promise.all([makeTestnetWallet(), makeTestnetWallet(), makeTestnetWallet(), makeTestnetWallet()])
owner_datawriter = new Datawriter({
receiveAddress: drainWallet.address,
sendWallet: ownerWallet,
xrpNode: xrpNode,
contractAddress: listeningWallet.address
})
user_datawriter = new Datawriter({
receiveAddress: drainWallet.address,
sendWallet: userWallet,
xrpNode: xrpNode,
contractAddress: listeningWallet.address
})
//await owner_dw.callEndpoint('submit', { title: "1", body: "2", from: "3" }, 100)
//await owner_dw.callEndpoint('submit', { title: "1", body: "2", from: "3" }, 100)
//await user_dw.callEndpoint('restricted', { title: "user", body: "user", from: "user" })
//await owner_dw.callEndpoint('restricted', { title: "owner", body: "owner", from: "owner" })
// #########################
// Define parameter types
// #########################
const shoutSchema = z.object({
title: z.string(),
body: z.string(),
from: z.string(),
id: z.optional(z.string())
})
type Shout = z.infer<typeof shoutSchema>
type State = {
shouts: Shout[]
}
// #########################
// Define endpoints
// #########################
type RJSVM_Endpoints = {
submit: (data: Shout) => void
restricted: (data: Shout) => void
}
// #########################
// Define init state
// #########################
abstract class RJSVM_Base
extends RJSVM<State, RJSVM_Endpoints>
implements RJSVM_Interface<RJSVM_Base> {
owner = ownerWallet.address
state: State = {
shouts: []
}
}
// #########################
// Implement logic
// #########################
const RJSVM_Contract: RJSVM_Implementations<RJSVM_Base> = {
submit: {
implementation: function (env, shout) {
this.state.shouts.unshift(shout)
},
visibility: 'public',
fee: 10,
parameterSchema: shoutSchema
},
restricted: {
implementation: function (env, shout) {
this.state.shouts.unshift(shout)
},
visibility: 'owner',
parameterSchema: shoutSchema
},
}
// #########################
// Build and connect
// #########################
const Rjsvm = RJSVM_Builder.from(RJSVM_Base, RJSVM_Contract);
const conf: RJSVM_Config = {
listeningAddress: listeningWallet.address,
rippleNode: xrpNode
}
rjsvm = new Rjsvm(conf)
await rjsvm.connect()
}
describe('RJSVM basic functions', () => {
before(async function () {
this.timeout(10000)
await setup()
})
after(async () => {
await rjsvm.disconnect()
})
it('Env contains the payload carrying transaction', function (done) {
this.timeout(30000)
makeTestnetWallet().then(async testWallet => {
//create a mock RJSVM
abstract class RJSVM_Base
extends RJSVM<undefined, { testEndpoint: () => void }>
implements RJSVM_Interface<RJSVM_Base> {
owner = ownerWallet.address
state = undefined
}
//Implementation of the 'test' endpoint
const RJSVM_Contract: RJSVM_Implementations<RJSVM_Base> = {
testEndpoint: {
implementation: function (env) {
expect(env.Account).to.be.equal(userWallet.address)
expect(env.Amount).to.be.equal('1')
expect(env.Destination).to.be.equal(testWallet.address)
expect(Number(env.Fee)).to.be.greaterThanOrEqual(10)
expect(env.LastLedgerSequence).to.be.a('number')
expect(env.Memos).to.be.an('Array')
const memo = JSON.parse(DataParser.hex_to_ascii(env.Memos[0].Memo.MemoData))
expect(memo.endpoint).to.be.equal('testEndpoint')
expect(env.Sequence).to.be.a('number')
expect(env.SigningPubKey).to.be.a('string')
expect(env.TransactionType).to.be.equal('Payment')
expect(env.TxnSignature).to.be.a('string')
expect(env.date).to.be.a('number')
expect(env.hash).to.be.a('string')
expect(env.inLedger).to.be.a('number')
expect(env.ledger_index).to.be.a('number')
runnable.disconnect()
done()
},
visibility: 'public',
parameterSchema: z.any()
},
}
const Rjsvm = RJSVM_Builder.from(RJSVM_Base, RJSVM_Contract)
const runnable = new Rjsvm({listeningAddress: testWallet.address,rippleNode: xrpNode})
await runnable.connect()
const dw = new Datawriter({
contractAddress: testWallet.address,
sendWallet: userWallet,
receiveAddress: drainWallet.address,
xrpNode: xrpNode
})
dw.callEndpoint('testEndpoint', "")
})
})
it('Called endpoint triggers in RJSVM', function (done) {
this.timeout(30000)
const data = { title: "1", body: "2", from: "3" };
rjsvm.once('submit', (payload) => {
expect(payload).to.be.an('object')
expect(payload).to.deep.equal(data)
done()
})
owner_datawriter.callEndpoint('submit', data, 10)
})
it('Calling an endpoint with insufficient fee fails', function (done) {
this.timeout(30000)
const data = { title: "f", body: "f", from: "f" };
rjsvm.once('error', (err) => {
expect(err).to.be.instanceOf(InsufficientFeeError)
done()
})
owner_datawriter.callEndpoint('submit', data, 1)
})
it('Restricted endpoint can be called by owner', function (done) {
this.timeout(30000)
const data = { title: "11", body: "22", from: "33" };
rjsvm.once('restricted', (payload) => {
expect(payload).to.be.an('object')
expect(payload).to.deep.equal(data)
done()
})
owner_datawriter.callEndpoint('restricted', data)
})
it('Restricted endpoint cannot be called by non-owner', function (done) {
this.timeout(30000)
const data = { title: "e", body: "e", from: "e" };
rjsvm.once('error', (err) => {
expect(err).to.be.instanceOf(RestrictedAccessError)
done()
})
user_datawriter.callEndpoint('restricted', data)
})
})
+16
View File
@@ -0,0 +1,16 @@
type Wallet = {secret: string, address:string }
export const makeTestnetWallet = () : Promise<Wallet> => fetch('https://faucet.altnet.rippletest.net/accounts', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then((raw:any) => {
return raw.json().then(content => {
return({
secret: content.account.secret,
address: content.account.address
});
})
});