Merge branch 'master' into hackathon

This commit is contained in:
Pavel Murygin 2022-10-26 17:05:28 +04:00
commit fb1fc409ff
8 changed files with 232 additions and 8 deletions

View File

@ -6,7 +6,7 @@ Official TypeScript implementation of the Fluence Peer.
## Getting started ## Getting started
To start developing applications with Fluence JS refer to the official [documentation](https://doc.fluence.dev/docs/js-sdk) To start developing applications with Fluence JS refer to the official [documentation](https://fluence.dev/docs/build/fluence-js/)
## Contributing ## Contributing

View File

@ -2,7 +2,7 @@
<a href="#fluence-js" id="fluence-js" style="color: inherit; text-decoration: none;"> <a href="#fluence-js" id="fluence-js" style="color: inherit; text-decoration: none;">
<h1>Fluence JS</h1> <h1>Fluence JS</h1>
</a> </a>
<p>To start developing applications with Fluence JS refer to the official <a href="https://doc.fluence.dev/docs/js-sdk">documentation</a></p> <p>To start developing applications with Fluence JS refer to the official <a href="https://fluence.dev/docs/build/fluence-js/">documentation</a></p>
<p>Fluence JS is an implementation of the Fluence protocol for JavaScript-based environments. It can connect browsers, Node.js applications, and so on to the Fluence p2p network.</p> <p>Fluence JS is an implementation of the Fluence protocol for JavaScript-based environments. It can connect browsers, Node.js applications, and so on to the Fluence p2p network.</p>
<p>Similar to the <a href="https://github.com/fluencelabs/fluence">Rust Fluence Peer implementation</a> it includes:</p> <p>Similar to the <a href="https://github.com/fluencelabs/fluence">Rust Fluence Peer implementation</a> it includes:</p>
<ul> <ul>
@ -11,6 +11,6 @@
<li>Builtin services</li> <li>Builtin services</li>
</ul> </ul>
<p>Fluence JS can call services and functions on the Fluence network, and expose new APIs to the p2p network directly from TypeScript and JavaScript. <p>Fluence JS can call services and functions on the Fluence network, and expose new APIs to the p2p network directly from TypeScript and JavaScript.
<a href="https://github.com/fluencelabs/aqua">Aqua language</a> uses Fluence JS as a compilation target, and they are designed to <a href="https://doc.fluence.dev/docs/js-sdk/3_in_depth#understanding-the-aqua-compiler-output">work in tandem</a>.</p> <a href="https://github.com/fluencelabs/aqua">Aqua language</a> uses Fluence JS as a compilation target, and they are designed to <a href="https://fluence.dev/docs/build/fluence-js/in-depth#understanding-the-aqua-compiler-output">work in tandem</a>.</p>
<p>Fluence JS can be used with any framework of your choice (or even without frameworks).</p> <p>Fluence JS can be used with any framework of your choice (or even without frameworks).</p>
</div></div><div class="col-4 col-menu menu-sticky-wrap menu-highlight"><nav class="tsd-navigation primary"><ul><li class="current"><a href="modules.html">Exports</a></li></ul></nav><nav class="tsd-navigation secondary menu-sticky"><ul><li class="tsd-kind-class"><a href="classes/FluencePeer.html" class="tsd-kind-icon">Fluence<wbr/>Peer</a></li><li class="tsd-kind-class"><a href="classes/KeyPair.html" class="tsd-kind-icon">Key<wbr/>Pair</a></li><li class="tsd-kind-interface tsd-has-type-parameter"><a href="interfaces/CallParams.html" class="tsd-kind-icon">Call<wbr/>Params</a></li><li class="tsd-kind-interface"><a href="interfaces/PeerConfig.html" class="tsd-kind-icon">Peer<wbr/>Config</a></li><li class="tsd-kind-type-alias"><a href="modules.html#AvmLoglevel" class="tsd-kind-icon">Avm<wbr/>Loglevel</a></li><li class="tsd-kind-type-alias"><a href="modules.html#PeerIdB58" class="tsd-kind-icon">Peer<wbr/>Id<wbr/>B58</a></li><li class="tsd-kind-type-alias"><a href="modules.html#PeerStatus" class="tsd-kind-icon">Peer<wbr/>Status</a></li><li class="tsd-kind-variable"><a href="modules.html#Fluence" class="tsd-kind-icon">Fluence</a></li><li class="tsd-kind-function tsd-is-external"><a href="modules.html#loadWasmFromFileSystem" class="tsd-kind-icon">load<wbr/>Wasm<wbr/>From<wbr/>File<wbr/>System</a></li><li class="tsd-kind-function tsd-is-external"><a href="modules.html#loadWasmFromNpmPackage" class="tsd-kind-icon">load<wbr/>Wasm<wbr/>From<wbr/>Npm<wbr/>Package</a></li><li class="tsd-kind-function tsd-is-external"><a href="modules.html#loadWasmFromServer" class="tsd-kind-icon">load<wbr/>Wasm<wbr/>From<wbr/>Server</a></li><li class="tsd-kind-function"><a href="modules.html#setLogLevel" class="tsd-kind-icon">set<wbr/>Log<wbr/>Level</a></li></ul></nav></div></div></div><footer class="with-border-bottom"><div class="container"><h2>Legend</h2><div class="tsd-legend-group"><ul class="tsd-legend"><li class="tsd-kind-constructor tsd-parent-kind-class"><span class="tsd-kind-icon">Constructor</span></li><li class="tsd-kind-property tsd-parent-kind-class"><span class="tsd-kind-icon">Property</span></li><li class="tsd-kind-method tsd-parent-kind-class"><span class="tsd-kind-icon">Method</span></li></ul><ul class="tsd-legend"><li class="tsd-kind-property tsd-parent-kind-interface"><span class="tsd-kind-icon">Property</span></li></ul><ul class="tsd-legend"><li class="tsd-kind-method tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static method</span></li></ul></div><h2>Settings</h2><p>Theme <select id="theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></p></div></footer><div class="container tsd-generator"><p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></div><div class="overlay"></div><script src="assets/main.js"></script></body></html> </div></div><div class="col-4 col-menu menu-sticky-wrap menu-highlight"><nav class="tsd-navigation primary"><ul><li class="current"><a href="modules.html">Exports</a></li></ul></nav><nav class="tsd-navigation secondary menu-sticky"><ul><li class="tsd-kind-class"><a href="classes/FluencePeer.html" class="tsd-kind-icon">Fluence<wbr/>Peer</a></li><li class="tsd-kind-class"><a href="classes/KeyPair.html" class="tsd-kind-icon">Key<wbr/>Pair</a></li><li class="tsd-kind-interface tsd-has-type-parameter"><a href="interfaces/CallParams.html" class="tsd-kind-icon">Call<wbr/>Params</a></li><li class="tsd-kind-interface"><a href="interfaces/PeerConfig.html" class="tsd-kind-icon">Peer<wbr/>Config</a></li><li class="tsd-kind-type-alias"><a href="modules.html#AvmLoglevel" class="tsd-kind-icon">Avm<wbr/>Loglevel</a></li><li class="tsd-kind-type-alias"><a href="modules.html#PeerIdB58" class="tsd-kind-icon">Peer<wbr/>Id<wbr/>B58</a></li><li class="tsd-kind-type-alias"><a href="modules.html#PeerStatus" class="tsd-kind-icon">Peer<wbr/>Status</a></li><li class="tsd-kind-variable"><a href="modules.html#Fluence" class="tsd-kind-icon">Fluence</a></li><li class="tsd-kind-function tsd-is-external"><a href="modules.html#loadWasmFromFileSystem" class="tsd-kind-icon">load<wbr/>Wasm<wbr/>From<wbr/>File<wbr/>System</a></li><li class="tsd-kind-function tsd-is-external"><a href="modules.html#loadWasmFromNpmPackage" class="tsd-kind-icon">load<wbr/>Wasm<wbr/>From<wbr/>Npm<wbr/>Package</a></li><li class="tsd-kind-function tsd-is-external"><a href="modules.html#loadWasmFromServer" class="tsd-kind-icon">load<wbr/>Wasm<wbr/>From<wbr/>Server</a></li><li class="tsd-kind-function"><a href="modules.html#setLogLevel" class="tsd-kind-icon">set<wbr/>Log<wbr/>Level</a></li></ul></nav></div></div></div><footer class="with-border-bottom"><div class="container"><h2>Legend</h2><div class="tsd-legend-group"><ul class="tsd-legend"><li class="tsd-kind-constructor tsd-parent-kind-class"><span class="tsd-kind-icon">Constructor</span></li><li class="tsd-kind-property tsd-parent-kind-class"><span class="tsd-kind-icon">Property</span></li><li class="tsd-kind-method tsd-parent-kind-class"><span class="tsd-kind-icon">Method</span></li></ul><ul class="tsd-legend"><li class="tsd-kind-property tsd-parent-kind-interface"><span class="tsd-kind-icon">Property</span></li></ul><ul class="tsd-legend"><li class="tsd-kind-method tsd-parent-kind-class tsd-is-static"><span class="tsd-kind-icon">Static method</span></li></ul></div><h2>Settings</h2><p>Theme <select id="theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></p></div></footer><div class="container tsd-generator"><p>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></div><div class="overlay"></div><script src="assets/main.js"></script></body></html>

View File

@ -6,7 +6,7 @@ Official TypeScript implementation of the Fluence Peer.
## Getting started ## Getting started
To start developing applications with Fluence JS refer to the official [documentation](https://doc.fluence.dev/docs/js-sdk) To start developing applications with Fluence JS refer to the official [documentation](https://fluence.dev/docs/build/fluence-js/)
## Contributing ## Contributing

View File

@ -1,6 +1,6 @@
{ {
"name": "@fluencelabs/fluence", "name": "@fluencelabs/fluence",
"version": "0.26.3-snapshot-3", "version": "0.26.3-snapshot-20",
"description": "TypeScript implementation of Fluence Peer", "description": "TypeScript implementation of Fluence Peer",
"main": "./dist/index.js", "main": "./dist/index.js",
"typings": "./dist/index.d.ts", "typings": "./dist/index.d.ts",

View File

@ -0,0 +1,75 @@
import { Particle } from '../../internal/Particle';
import { doNothing } from '../../internal/utils';
import { FluencePeer } from '../../index';
let peer: FluencePeer;
describe('Sig service test suite', () => {
afterEach(async () => {
if (peer) {
await peer.stop();
}
});
beforeEach(async () => {
peer = new FluencePeer();
await peer.start();
});
it('JSON builtin spec', async () => {
const script = `
(seq
(seq
(seq
;; create
(seq
(call %init_peer_id% ("json" "obj") ["name" "nested_first" "num" 1] nested_first)
(call %init_peer_id% ("json" "obj") ["name" "nested_second" "num" 2] nested_second)
)
(call %init_peer_id% ("json" "obj") ["name" "outer_first" "num" 0 "nested" nested_first] outer_first)
)
(seq
;; modify
(seq
(call %init_peer_id% ("json" "put") [outer_first "nested" nested_second] outer_tmp_second)
(call %init_peer_id% ("json" "puts") [outer_tmp_second "name" "outer_second" "num" 3] outer_second)
)
;; stringify and parse
(seq
(call %init_peer_id% ("json" "stringify") [outer_first] outer_first_string)
(call %init_peer_id% ("json" "parse") [outer_first_string] outer_first_parsed)
)
)
)
(call %init_peer_id% ("res" "res") [nested_first nested_second outer_first outer_second outer_first_string outer_first_parsed])
)
`;
const promise = new Promise<any>((resolve) => {
peer.internals.regHandler.common('res', 'res', (req) => {
resolve(req.args);
return {
result: {},
retCode: 0,
};
});
});
const p = peer.internals.createNewParticle(script) as Particle;
await peer.internals.initiateParticle(p, doNothing);
const [nestedFirst, nestedSecond, outerFirst, outerSecond, outerFirstString, outerFirstParsed] = await promise;
const nfExpected = { name: 'nested_first', num: 1 };
const nsExpected = { name: 'nested_second', num: 2 };
const ofExpected = { name: 'outer_first', nested: nfExpected, num: 0 };
const ofString = JSON.stringify(ofExpected);
const osExpected = { name: 'outer_second', num: 3, nested: nsExpected };
expect(nestedFirst).toMatchObject(nfExpected);
expect(nestedSecond).toMatchObject(nsExpected);
expect(outerFirst).toMatchObject(ofExpected);
expect(outerSecond).toMatchObject(osExpected);
expect(outerFirstParsed).toMatchObject(ofExpected);
expect(outerFirstString).toBe(ofString);
});
});

View File

@ -111,6 +111,29 @@ describe('Tests for default handler', () => {
${'array'} | ${'diff'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a"]} ${'array'} | ${'diff'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a"]}
${'array'} | ${'sdiff'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a", "d"]} ${'array'} | ${'sdiff'}" | ${[["a", "b", "c"], ["c", "b", "d"]]} | ${0} | ${["a", "d"]}
${'json'} | ${'obj'}" | ${["a", 10, "b", "string", "c", null]} | ${0} | ${{a: 10, b: "string", c: null}}
${'json'} | ${'obj'}" | ${["a", 10, "b", "string", "c"]} | ${1} | ${"Expected even number of argument(s). Got 5"}
${'json'} | ${'obj'}" | ${[]} | ${0} | ${{}}
${'json'} | ${'put'}" | ${[{}, "a", 10]} | ${0} | ${{a: 10}}
${'json'} | ${'put'}" | ${[{b: 11}, "a", 10]} | ${0} | ${{a: 10, b: 11}}
${'json'} | ${'put'}" | ${["a", "a", 11]} | ${1} | ${"Argument 0 expected to be of type object, Got string"}
${'json'} | ${'put'}" | ${[{}, "a", 10, "b", 20]} | ${1} | ${"Expected 3 argument(s). Got 5"}
${'json'} | ${'put'}" | ${[{}]} | ${1} | ${"Expected 3 argument(s). Got 1"}
${'json'} | ${'puts'}" | ${[{}, "a", 10]} | ${0} | ${{a: 10}}
${'json'} | ${'puts'}" | ${[{b: 11}, "a", 10]} | ${0} | ${{a: 10, b: 11}}
${'json'} | ${'puts'}" | ${[{}, "a", 10, "b", "string", "c", null]} | ${0} | ${{a: 10, b: "string", c: null}}
${'json'} | ${'puts'}" | ${[{x: "text"}, "a", 10, "b", "string"]} | ${0} | ${{a: 10, b: "string", x: "text"}}
${'json'} | ${'puts'}" | ${[{}]} | ${1} | ${"Expected more than 3 argument(s). Got 1"}
${'json'} | ${'puts'}" | ${["a", "a", 11]} | ${1} | ${"Argument 0 expected to be of type object, Got string"}
${'json'} | ${'stringify'}" | ${[{a: 10, b: "string", c: null}]} | ${0} | ${"{\"a\":10,\"b\":\"string\",\"c\":null}"}
${'json'} | ${'stringify'}" | ${[1]} | ${1} | ${"Argument 0 expected to be of type object, Got number"}
${'json'} | ${'parse'}" | ${["{\"a\":10,\"b\":\"string\",\"c\":null}"]} | ${0} | ${{a: 10, b: "string", c: null}}
${'json'} | ${'parse'}" | ${["incorrect"]} | ${1} | ${"Unexpected token i in JSON at position 0"}
${'json'} | ${'parse'}" | ${[10]} | ${1} | ${"Argument 0 expected to be of type string, Got number"}
`.test( `.test(
// //
'$fnName with $args expected retcode: $retCode and result: $result', '$fnName with $args expected retcode: $retCode and result: $result',

View File

@ -18,7 +18,7 @@ import { encode, decode } from 'bs58';
import { sha256 } from 'multiformats/hashes/sha2'; import { sha256 } from 'multiformats/hashes/sha2';
import { CallServiceResult } from '@fluencelabs/avm'; import { CallServiceResult } from '@fluencelabs/avm';
import { GenericCallServiceHandler, ResultCodes } from '../commonTypes'; import { CallServiceData, GenericCallServiceHandler, ResultCodes } from '../commonTypes';
import { jsonify } from '../utils'; import { jsonify } from '../utils';
import Buffer from '../Buffer'; import Buffer from '../Buffer';
@ -40,6 +40,23 @@ const errorNotImpl = (methodName: string) => {
return error(`The JS implementation of Peer does not support "${methodName}"`); return error(`The JS implementation of Peer does not support "${methodName}"`);
}; };
const makeJsonImpl = (args: Array<any>) => {
const [obj, ...kvs] = args;
const toMerge: Record<string, any> = {};
for (let i = 0; i < kvs.length / 2; i++) {
const k = kvs[i * 2];
if (!isString(k)) {
return error(`Argument ${k} is expected to be string`);
}
const v = kvs[i * 2 + 1];
toMerge[k] = v;
}
const res = { ...obj, ...toMerge };
return success(res);
};
export const builtInServices: Record<string, Record<string, GenericCallServiceHandler>> = { export const builtInServices: Record<string, Record<string, GenericCallServiceHandler>> = {
peer: { peer: {
identify: () => { identify: () => {
@ -467,6 +484,81 @@ export const builtInServices: Record<string, Record<string, GenericCallServiceHa
return success(sdiff); return success(sdiff);
}, },
}, },
json: {
obj: (req) => {
let err;
if ((err = checkForArgumentsCountEven(req, 1))) {
return err;
}
return makeJsonImpl([{}, ...req.args]);
},
put: (req) => {
let err;
if ((err = checkForArgumentsCount(req, 3))) {
return err;
}
if((err = checkForArgumentType(req, 0, "object"))) {
return err;
}
return makeJsonImpl(req.args);
},
puts: (req) => {
let err;
if ((err = checkForArgumentsCountOdd(req, 1))) {
return err;
}
if ((err = checkForArgumentsCountMoreThan(req, 3))) {
return err;
}
if((err = checkForArgumentType(req, 0, "object"))) {
return err;
}
return makeJsonImpl(req.args);
},
stringify: (req) => {
let err;
if ((err = checkForArgumentsCount(req, 1))) {
return err;
}
if((err = checkForArgumentType(req, 0, "object"))) {
return err;
}
const [json] = req.args;
const res = JSON.stringify(json);
return success(res);
},
parse: (req) => {
let err;
if ((err = checkForArgumentsCount(req, 1))) {
return err;
}
if((err = checkForArgumentType(req, 0, "string"))) {
return err;
}
const [raw] = req.args;
try{
const json = JSON.parse(raw);
return success(json);
} catch(err: any) {
return error(err.message);
}
},
},
} as const; } as const;
const checkForArgumentsCount = (req: { args: Array<unknown> }, count: number) => { const checkForArgumentsCount = (req: { args: Array<unknown> }, count: number) => {
@ -474,3 +566,37 @@ const checkForArgumentsCount = (req: { args: Array<unknown> }, count: number) =>
return error(`Expected ${count} argument(s). Got ${req.args.length}`); return error(`Expected ${count} argument(s). Got ${req.args.length}`);
} }
}; };
const checkForArgumentsCountMoreThan = (req: { args: Array<unknown> }, count: number) => {
if (req.args.length < count) {
return error(`Expected more than ${count} argument(s). Got ${req.args.length}`);
}
};
const checkForArgumentsCountEven = (req: { args: Array<unknown> }, count: number) => {
if (req.args.length % 2 === 1) {
return error(`Expected even number of argument(s). Got ${req.args.length}`);
}
};
const checkForArgumentsCountOdd = (req: { args: Array<unknown> }, count: number) => {
if (req.args.length % 2 === 0) {
return error(`Expected odd number of argument(s). Got ${req.args.length}`);
}
};
const checkForArgumentType = (req: { args: Array<unknown> }, index: number, type: string) => {
const actual = typeof req.args[index];
if (actual !== type) {
return error(`Argument ${index} expected to be of type ${type}, Got ${actual}`);
}
};
export const isString = (unknown: unknown): unknown is string => {
return unknown !== null && typeof unknown === 'string';
};
export const isObject = (unknown: unknown): unknown is object => {
return unknown !== null && typeof unknown === 'object';
};

View File

@ -1,6 +1,6 @@
# Fluence JS # Fluence JS
To start developing applications with Fluence JS refer to the official [documentation](https://doc.fluence.dev/docs/js-sdk) To start developing applications with Fluence JS refer to the official [documentation](https://fluence.dev/docs/build/fluence-js/)
Fluence JS is an implementation of the Fluence protocol for JavaScript-based environments. It can connect browsers, Node.js applications, and so on to the Fluence p2p network. Fluence JS is an implementation of the Fluence protocol for JavaScript-based environments. It can connect browsers, Node.js applications, and so on to the Fluence p2p network.
@ -11,6 +11,6 @@ Similar to the [Rust Fluence Peer implementation](https://github.com/fluencelabs
- Builtin services - Builtin services
Fluence JS can call services and functions on the Fluence network, and expose new APIs to the p2p network directly from TypeScript and JavaScript. Fluence JS can call services and functions on the Fluence network, and expose new APIs to the p2p network directly from TypeScript and JavaScript.
[Aqua language](https://github.com/fluencelabs/aqua) uses Fluence JS as a compilation target, and they are designed to [work in tandem](https://doc.fluence.dev/docs/js-sdk/3_in_depth#understanding-the-aqua-compiler-output). [Aqua language](https://github.com/fluencelabs/aqua) uses Fluence JS as a compilation target, and they are designed to [work in tandem](https://fluence.dev/docs/build/fluence-js/in-depth#understanding-the-aqua-compiler-output).
Fluence JS can be used with any framework of your choice \(or even without frameworks\). Fluence JS can be used with any framework of your choice \(or even without frameworks\).