From cb0a934f31d33dd0caa1d90abf49d82617039de9 Mon Sep 17 00:00:00 2001 From: shamsartem Date: Wed, 26 Oct 2022 11:45:31 +0300 Subject: [PATCH 1/2] update links (#194) --- README.md | 2 +- docs/index.html | 4 ++-- packages/fluence-js/README.md | 2 +- typedoc.md | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0a9dbdf7..ed33792b 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/index.html b/docs/index.html index 3febec9b..f585759a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,7 +2,7 @@

Fluence JS

-

To start developing applications with Fluence JS refer to the official documentation

+

To start developing applications with Fluence JS refer to the official documentation

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.

Similar to the Rust Fluence Peer implementation it includes:

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 uses Fluence JS as a compilation target, and they are designed to work in tandem.

+Aqua language uses Fluence JS as a compilation target, and they are designed to work in tandem.

Fluence JS can be used with any framework of your choice (or even without frameworks).

Generated using TypeDoc

\ No newline at end of file diff --git a/packages/fluence-js/README.md b/packages/fluence-js/README.md index 94c25747..c1052b60 100644 --- a/packages/fluence-js/README.md +++ b/packages/fluence-js/README.md @@ -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 diff --git a/typedoc.md b/typedoc.md index 1923578a..5916cd78 100644 --- a/typedoc.md +++ b/typedoc.md @@ -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\). From 3ef88d99056a1a6396ea9a76434ba1772442f7a3 Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 26 Oct 2022 16:02:28 +0300 Subject: [PATCH 2/2] Implement `json` builtin (#195) --- packages/fluence-js/package.json | 2 +- .../__test__/integration/jsonBuiltin.spec.ts | 75 ++++++++++ .../src/__test__/unit/builtInHandler.spec.ts | 23 ++++ .../src/internal/builtins/common.ts | 128 +++++++++++++++++- 4 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 packages/fluence-js/src/__test__/integration/jsonBuiltin.spec.ts diff --git a/packages/fluence-js/package.json b/packages/fluence-js/package.json index 7b065df0..6d846a3d 100644 --- a/packages/fluence-js/package.json +++ b/packages/fluence-js/package.json @@ -1,6 +1,6 @@ { "name": "@fluencelabs/fluence", - "version": "0.26.2", + "version": "0.26.3", "description": "TypeScript implementation of Fluence Peer", "main": "./dist/index.js", "typings": "./dist/index.d.ts", diff --git a/packages/fluence-js/src/__test__/integration/jsonBuiltin.spec.ts b/packages/fluence-js/src/__test__/integration/jsonBuiltin.spec.ts new file mode 100644 index 00000000..3bfcfb9c --- /dev/null +++ b/packages/fluence-js/src/__test__/integration/jsonBuiltin.spec.ts @@ -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((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); + }); +}); diff --git a/packages/fluence-js/src/__test__/unit/builtInHandler.spec.ts b/packages/fluence-js/src/__test__/unit/builtInHandler.spec.ts index 6e4e0af4..0e5cb1e4 100644 --- a/packages/fluence-js/src/__test__/unit/builtInHandler.spec.ts +++ b/packages/fluence-js/src/__test__/unit/builtInHandler.spec.ts @@ -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', diff --git a/packages/fluence-js/src/internal/builtins/common.ts b/packages/fluence-js/src/internal/builtins/common.ts index 61781b36..0a0cd8af 100644 --- a/packages/fluence-js/src/internal/builtins/common.ts +++ b/packages/fluence-js/src/internal/builtins/common.ts @@ -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) => { + const [obj, ...kvs] = args; + + const toMerge: Record = {}; + 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> = { peer: { identify: () => { @@ -467,6 +484,81 @@ export const builtInServices: Record { + 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 }, count: number) => { @@ -474,3 +566,37 @@ const checkForArgumentsCount = (req: { args: Array }, count: number) => return error(`Expected ${count} argument(s). Got ${req.args.length}`); } }; + +const checkForArgumentsCountMoreThan = (req: { args: Array }, count: number) => { + if (req.args.length < count) { + return error(`Expected more than ${count} argument(s). Got ${req.args.length}`); + } +}; + +const checkForArgumentsCountEven = (req: { args: Array }, 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 }, 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 }, 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'; +}; +