Comparing version 0.0.37 to 0.0.38
{ | ||
"name": "webdetta", | ||
"version": "0.0.37", | ||
"version": "0.0.38", | ||
"author": "Fedot Kriutchenko <fodyadev@gmail.com>", | ||
@@ -5,0 +5,0 @@ "description": "", |
import FunctionParser from 'parse-function'; | ||
import decodeFunction from './decode-function.js'; | ||
const parser = FunctionParser(); | ||
const encodeFunction = val => { | ||
const { args, defaults, body } = parser.parse(val); | ||
const args_ = args | ||
.map(d => defaults[d] ? d + '=' + defaults[d] : d) | ||
.join(', '); | ||
return { args: args_, body }; | ||
} | ||
export const SdkClient = (rpc, entries) => { | ||
const encodeObj = (obj) => | ||
JSON.stringify(Object.fromEntries(Object.entries(obj).map(([k, v]) => [ | ||
k, | ||
typeof v == 'function' | ||
? { type: 'function', value: encodeFunction(v) } | ||
: { value: v } | ||
]))); | ||
const decodeObj = (rpc, str) => | ||
Object.fromEntries(Object.entries(JSON.parse(str)).map(([k, v]) => [ | ||
k, | ||
v.type == 'function' | ||
? decodeFunction(rpc, instance, v.args, v.body) | ||
: v.value | ||
])); | ||
export const SdkInstance = (rpc, entries) => { | ||
const instance = {}; | ||
@@ -12,4 +37,12 @@ const define = (path, descriptor) => { | ||
for (const e of entries) { | ||
if (!e.isEncoded) continue; | ||
e.rpcHandler = e.rpcHandler && | ||
decodeFunction(rpc, instance, e.rpcHandler.args, e.rpcHandler.body); | ||
e.propertyDescriptor = | ||
decodeObj(rpc, instance, e.propertyDescriptor); | ||
} | ||
for (const { path, handlerId, rpcHandler, propertyDescriptor } of entries) { | ||
rpc.methods[handlerId] = rpcHandler; | ||
if (rpcHandler && rpc) rpc.methods[handlerId] = rpcHandler; | ||
define(path, propertyDescriptor); | ||
@@ -22,28 +55,29 @@ } | ||
class SdkEntry { | ||
constructor({ client, server }) { | ||
Object.assign(this, { client, server }); | ||
} | ||
} | ||
const validateHandler = (val) => { | ||
if (typeof val == 'object') { | ||
for (const k of Object.keys(val)) { | ||
validateHandler(val[k]); | ||
} | ||
} else if (typeof val != 'function') { | ||
const traverseSdkObject = (val, path=[], res=[]) => { | ||
if (typeof val == 'object') | ||
for (const [k, v] of Object.entries(val)) | ||
traverseSdkObject(v, [...path, k], res); | ||
else if (typeof val == 'function') | ||
res.push({ path: path, val }); | ||
else | ||
throw new Error([ | ||
'ServerHandler value must be a function ', | ||
'or a nested plain object containing a collection of functions.' | ||
'The value must be a function ', | ||
'or a nested object containing a collection of functions.' | ||
].join('')); | ||
} | ||
return res; | ||
} | ||
const serializeFunction = val => { | ||
const { args, defaults, body } = parser.parse(val); | ||
const args_ = args | ||
.map(d => defaults[d] ? d + '=' + defaults[d] : d) | ||
.join(', '); | ||
return { args: args_, body }; | ||
} | ||
const SDK_ENTRY = Symbol('SDK_ENTRY'); | ||
const SdkEntry = (func) => val => ({ | ||
[SDK_ENTRY]: true, | ||
list: [ | ||
{ path: [], ...func(val) } | ||
] | ||
}); | ||
const NestedSdkEntry = (func) => val => ({ | ||
[SDK_ENTRY]: true, | ||
list: traverseSdkObject(val).map(d => ( | ||
{ path: d.path, ...func(d.val) } | ||
)) | ||
}); | ||
@@ -60,4 +94,3 @@ const remoteFunction = (handlerId, args) => { | ||
const localFunction = (args, body) => { | ||
const handler = new Function(...args, body); | ||
const localFunction = (handler) => { | ||
return { | ||
@@ -69,45 +102,32 @@ rpcHandler: handler, | ||
export const ClientHandler = (val) => { | ||
const { args, body } = serializeFunction(val); | ||
return new SdkEntry({ | ||
client: (handlerId) => localFunction(args, body), | ||
export const ClientHandler = NestedSdkEntry((val) => { | ||
const { args, body } = encodeFunction(val); | ||
return { | ||
client: (handlerId) => localFunction(new Function(...args, body)), | ||
server: (handlerId) => remoteFunction(handlerId, args), | ||
}); | ||
} | ||
}; | ||
}); | ||
export const ServerHandler = (val) => { | ||
const { args, body } = serializeFunction(val); | ||
return new SdkEntry({ | ||
export const ServerHandler = NestedSdkEntry((val) => { | ||
const { args, body } = encodeFunction(val); | ||
return { | ||
client: (handlerId) => remoteFunction(handlerId, args), | ||
server: (handlerId) => localFunction(args, body) | ||
}); | ||
} | ||
server: (handlerId) => localFunction(val) | ||
}; | ||
}); | ||
export const SdkServer = (sdkDefinition) => { | ||
const handlers = {}; | ||
const clientEntries = []; | ||
const clientCode = rpcURL => [ | ||
`import { RpcClient } from 'webdetta/rpc/client';`, | ||
`import { SdkClient } from 'webdetta/rpc-api/sdk';`, | ||
`import { SdkInstance } from 'webdetta/rpc-api/sdk';`, | ||
`const rpc = RpcClient("${rpcURL}");`, | ||
`export default { chats: { list: () => [1,2,3] } };` | ||
`export default SdkInstance(rpc, ${JSON.stringify(clientEntries, null, 2)});` | ||
].join('\n'); | ||
const traverseHandler = (val, path=[], res=[]) => { | ||
if (typeof val == 'object') { | ||
for (const [k, v] of Object.entries(val)) { | ||
traverseHandler(v, [...path, k], res); | ||
} | ||
} | ||
if (typeof val == 'function') { | ||
const { args, defaults, body } = parser.parse(val); | ||
const args_ = args | ||
.map(d => defaults[d] ? d + '=' + defaults[d] : d) | ||
.join(', '); | ||
res.push({ path: path, args: args_, body: body }); | ||
} | ||
return res; | ||
} | ||
const serverMethods = {}; | ||
const serverEntries = []; | ||
for (const [key, entry] of Object.entries(sdkDefinition)) { | ||
if (!(entry instanceof Entry)) { | ||
if (!(SDK_ENTRY in entry)) { | ||
throw new Error([ | ||
@@ -118,6 +138,28 @@ 'SDK entries must be decorated with one of the following functions: ', | ||
} | ||
console.log(entry); | ||
for (const d of entry.list) { | ||
const fullpath = [key, ...d.path]; | ||
const handlerId = fullpath.join(','); | ||
const cli = d.client(handlerId); | ||
clientEntries.push({ | ||
path: fullpath, handlerId, isEncoded: true, | ||
rpcHandler: cli.rpcHandler && encodeFunction(cli.rpcHandler), | ||
propertyDescriptor: encodeObj(cli.propertyDescriptor) | ||
}); | ||
const srv = d.server(handlerId); | ||
serverEntries.push({ | ||
path: fullpath, handlerId, isEncoded: false, | ||
rpcHandler: srv.rpcHandler, | ||
propertyDescriptor: srv.propertyDescriptor | ||
}); | ||
} | ||
} | ||
return { handlers, clientCode }; | ||
for (const d of clientEntries) console.log('---', d); | ||
for (const d of serverEntries) console.log('+++', d); | ||
SdkInstance({ methods: serverMethods }, serverEntries); | ||
console.log(clientCode('localhost')); | ||
return { serverMethods, clientCode }; | ||
} |
@@ -93,3 +93,3 @@ import { safe } from '../common/func.js'; | ||
sdk: (path, { methods }) => { | ||
sdk: (path, methods) => { | ||
const { handlers, clientCode } = SdkServer(methods); | ||
@@ -96,0 +96,0 @@ instance.httpHandler.get(path, (req, res) => { |
30633
28
883