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
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

View File

@ -2,7 +2,7 @@
<a href="#fluence-js" id="fluence-js" style="color: inherit; text-decoration: none;">
<h1>Fluence JS</h1>
</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>Similar to the <a href="https://github.com/fluencelabs/fluence">Rust Fluence Peer implementation</a> it includes:</p>
<ul>
@ -11,6 +11,6 @@
<li>Builtin services</li>
</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.
<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>
</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
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

View File

@ -1,6 +1,6 @@
{
"name": "@fluencelabs/fluence",
"version": "0.26.3-snapshot-3",
"version": "0.26.3-snapshot-20",
"description": "TypeScript implementation of Fluence Peer",
"main": "./dist/index.js",
"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'} | ${'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(
//
'$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 { CallServiceResult } from '@fluencelabs/avm';
import { GenericCallServiceHandler, ResultCodes } from '../commonTypes';
import { CallServiceData, GenericCallServiceHandler, ResultCodes } from '../commonTypes';
import { jsonify } from '../utils';
import Buffer from '../Buffer';
@ -40,6 +40,23 @@ const errorNotImpl = (methodName: string) => {
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>> = {
peer: {
identify: () => {
@ -467,6 +484,81 @@ export const builtInServices: Record<string, Record<string, GenericCallServiceHa
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;
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}`);
}
};
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
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.
@ -11,6 +11,6 @@ Similar to the [Rust Fluence Peer implementation](https://github.com/fluencelabs
- 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.
[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\).