@livon/client 0.27.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ clientModule: ()=>clientModule,
28
+ createClient: ()=>createClient,
29
+ createClientModule: ()=>createClientModule
30
+ });
31
+ const DEFAULT_REQUEST_KEY = 'livon.client.request';
32
+ const setClientRequest = ({ client, registry, requestKey })=>{
33
+ if ('object' != typeof client || null === client || !('setRequest' in client)) return;
34
+ const candidate = client;
35
+ if ('function' != typeof candidate.setRequest) return;
36
+ candidate.setRequest((event, payload)=>{
37
+ const request = registry.state.get(requestKey);
38
+ if (!request) throw new Error('Client request handler is not available.');
39
+ return request(event, payload);
40
+ });
41
+ };
42
+ const buildClientEventEnvelope = (envelope)=>{
43
+ if ('payload' in envelope) return {
44
+ ...envelope,
45
+ payload: envelope.payload
46
+ };
47
+ return {
48
+ ...envelope,
49
+ error: envelope.error
50
+ };
51
+ };
52
+ const clientModule = (client, options = {})=>{
53
+ const register = (registry)=>{
54
+ const requestKey = options.requestKey ?? DEFAULT_REQUEST_KEY;
55
+ setClientRequest({
56
+ client,
57
+ registry,
58
+ requestKey
59
+ });
60
+ registry.onReceive((envelope, _ctx, next)=>{
61
+ client.emitEvent(buildClientEventEnvelope(envelope));
62
+ return next();
63
+ });
64
+ };
65
+ return {
66
+ name: options.name ?? 'client-module',
67
+ register
68
+ };
69
+ };
70
+ const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
71
+ const isArray = (value)=>Array.isArray(value);
72
+ const capitalize = (value)=>0 === value.length ? value : value.slice(0, 1).toUpperCase() + value.slice(1);
73
+ const camelCaseName = (value)=>{
74
+ if (!value) return value;
75
+ return value.replace(/[^a-zA-Z0-9]+(.)/g, (_, group)=>String(group).toUpperCase()).replace(/^./, (char)=>char.toLowerCase());
76
+ };
77
+ const fieldMethodName = (owner, field)=>`$${camelCaseName(owner)}${capitalize(field)}`;
78
+ const fieldEventName = (owner, field)=>`$${owner}.${field}`;
79
+ const walkAst = (node, visit)=>{
80
+ visit(node);
81
+ node.children?.forEach((child)=>walkAst(child, visit));
82
+ };
83
+ const collectOperations = (root)=>{
84
+ const operations = [];
85
+ walkAst(root, (node)=>{
86
+ if ('operation' !== node.type || !node.name) return;
87
+ const input = node.children?.[0];
88
+ const output = node.children?.[1];
89
+ operations.push({
90
+ name: node.name,
91
+ input,
92
+ output,
93
+ event: node.name
94
+ });
95
+ });
96
+ return operations;
97
+ };
98
+ const collectFieldOperations = (root)=>{
99
+ const registry = new Map();
100
+ walkAst(root, (node)=>{
101
+ if ('field' !== node.type) return;
102
+ const constraints = node.constraints;
103
+ const owner = 'string' == typeof constraints?.owner ? constraints.owner : void 0;
104
+ const field = 'string' == typeof constraints?.field ? constraints.field : void 0;
105
+ if (!owner || !field) return;
106
+ const children = node.children ?? [];
107
+ const dependsOn = children[0];
108
+ const input = 3 === children.length ? children[1] : void 0;
109
+ const output = 3 === children.length ? children[2] : children[1];
110
+ if (!dependsOn) return;
111
+ const spec = {
112
+ owner,
113
+ field,
114
+ input,
115
+ output,
116
+ event: fieldEventName(owner, field)
117
+ };
118
+ if (!registry.has(owner)) registry.set(owner, new Map());
119
+ registry.get(owner).set(field, spec);
120
+ });
121
+ return registry;
122
+ };
123
+ const hydrateByNode = ({ value, node, registry, request })=>{
124
+ if (!node) return value;
125
+ if ('array' === node.type && isArray(value)) {
126
+ const child = node.children?.[0];
127
+ value.forEach((item, index)=>{
128
+ value[index] = hydrateByNode({
129
+ value: item,
130
+ node: child,
131
+ registry,
132
+ request
133
+ });
134
+ });
135
+ return value;
136
+ }
137
+ if ('tuple' === node.type && isArray(value)) {
138
+ const children = node.children ?? [];
139
+ value.forEach((item, index)=>{
140
+ value[index] = hydrateByNode({
141
+ value: item,
142
+ node: children[index],
143
+ registry,
144
+ request
145
+ });
146
+ });
147
+ return value;
148
+ }
149
+ if ('object' === node.type && isRecord(value)) {
150
+ const typeName = node.name;
151
+ if (typeName && registry.has(typeName)) attachFieldOperations({
152
+ target: value,
153
+ typeName,
154
+ registry,
155
+ request
156
+ });
157
+ const fields = node.children ?? [];
158
+ fields.forEach((fieldNode)=>{
159
+ if ('field' !== fieldNode.type || !fieldNode.name) return;
160
+ const child = fieldNode.children?.[0];
161
+ if (!child) return;
162
+ value[fieldNode.name] = hydrateByNode({
163
+ value: value[fieldNode.name],
164
+ node: child,
165
+ registry,
166
+ request
167
+ });
168
+ });
169
+ return value;
170
+ }
171
+ if ('field' === node.type) {
172
+ const child = node.children?.[0];
173
+ return hydrateByNode({
174
+ value,
175
+ node: child,
176
+ registry,
177
+ request
178
+ });
179
+ }
180
+ return value;
181
+ };
182
+ const attachFieldOperations = ({ target, typeName, registry, request })=>{
183
+ const operations = registry.get(typeName);
184
+ if (!operations) return;
185
+ if (!Object.isExtensible(target)) return;
186
+ operations.forEach((spec, fieldName)=>{
187
+ if (fieldName in target) return;
188
+ Object.defineProperty(target, fieldName, {
189
+ enumerable: false,
190
+ configurable: true,
191
+ value: async (input)=>{
192
+ const payload = {
193
+ dependsOn: target,
194
+ input
195
+ };
196
+ const result = await request(spec.event, payload);
197
+ return hydrateByNode({
198
+ value: result,
199
+ node: spec.output,
200
+ registry,
201
+ request
202
+ });
203
+ }
204
+ });
205
+ });
206
+ };
207
+ const normalizeFieldPayload = (payload)=>{
208
+ if (isRecord(payload) && 'dependsOn' in payload) {
209
+ const dependsOn = payload.dependsOn;
210
+ const input = 'input' in payload ? payload.input : void 0;
211
+ if (void 0 === input) return {
212
+ dependsOn
213
+ };
214
+ return {
215
+ dependsOn,
216
+ input
217
+ };
218
+ }
219
+ return {
220
+ dependsOn: payload
221
+ };
222
+ };
223
+ const createClientCore = ({ ast })=>{
224
+ const operations = collectOperations(ast);
225
+ const fieldRegistry = collectFieldOperations(ast);
226
+ let request;
227
+ const client = {};
228
+ client.setRequest = (next)=>{
229
+ request = next;
230
+ };
231
+ const globalHandlers = new Map();
232
+ const globalEnabled = new Map();
233
+ const roomHandlers = new Map();
234
+ const roomEnabled = new Map();
235
+ const registerHandlers = (handlers, roomId)=>{
236
+ const entries = Object.entries(handlers).filter(([, handler])=>'function' == typeof handler);
237
+ if (0 === entries.length) return;
238
+ if (!roomId) return void entries.forEach(([event, handler])=>{
239
+ globalHandlers.set(event, handler);
240
+ globalEnabled.set(event, true);
241
+ });
242
+ const roomMap = roomHandlers.get(roomId) ?? new Map();
243
+ const enabledMap = roomEnabled.get(roomId) ?? new Map();
244
+ entries.forEach(([event, handler])=>{
245
+ roomMap.set(event, handler);
246
+ enabledMap.set(event, true);
247
+ });
248
+ roomHandlers.set(roomId, roomMap);
249
+ roomEnabled.set(roomId, enabledMap);
250
+ };
251
+ const toggleHandler = ({ enabled, event, roomId })=>{
252
+ if (!roomId) return void globalEnabled.set(event, enabled);
253
+ const enabledMap = roomEnabled.get(roomId) ?? new Map();
254
+ enabledMap.set(event, enabled);
255
+ roomEnabled.set(roomId, enabledMap);
256
+ };
257
+ const dispatch = (envelope)=>{
258
+ const ctx = {
259
+ eventId: envelope.id,
260
+ event: envelope.event,
261
+ status: envelope.status,
262
+ metadata: envelope.metadata,
263
+ context: envelope.context,
264
+ room: 'string' == typeof envelope.metadata?.room ? String(envelope.metadata.room) : void 0
265
+ };
266
+ const roomId = ctx.room;
267
+ if (roomId) {
268
+ const enabledMap = roomEnabled.get(roomId);
269
+ const roomMap = roomHandlers.get(roomId);
270
+ const handler = roomMap?.get(envelope.event);
271
+ const isEnabled = enabledMap?.get(envelope.event) ?? true;
272
+ if (handler && isEnabled) handler(envelope.payload, ctx);
273
+ }
274
+ const handler = globalHandlers.get(envelope.event);
275
+ const isEnabled = globalEnabled.get(envelope.event) ?? true;
276
+ if (handler && isEnabled) handler(envelope.payload, ctx);
277
+ };
278
+ operations.forEach((op)=>{
279
+ client[op.name] = async (input)=>{
280
+ if (!request) throw new Error('Client request handler is not available.');
281
+ const result = await request(op.event, input);
282
+ return hydrateByNode({
283
+ value: result,
284
+ node: op.output,
285
+ registry: fieldRegistry,
286
+ request
287
+ });
288
+ };
289
+ });
290
+ fieldRegistry.forEach((fields, owner)=>{
291
+ fields.forEach((spec, field)=>{
292
+ const method = fieldMethodName(owner, field);
293
+ if (method in client) return;
294
+ client[method] = async (payload, input)=>{
295
+ const normalized = normalizeFieldPayload(payload);
296
+ if (!request) throw new Error('Client request handler is not available.');
297
+ const result = await request(spec.event, {
298
+ dependsOn: normalized.dependsOn,
299
+ input: input ?? normalized.input
300
+ });
301
+ return hydrateByNode({
302
+ value: result,
303
+ node: spec.output,
304
+ registry: fieldRegistry,
305
+ request
306
+ });
307
+ };
308
+ });
309
+ });
310
+ return Object.assign(client, {
311
+ __register: registerHandlers,
312
+ __toggle: (event, enabled, roomId)=>toggleHandler({
313
+ event,
314
+ enabled,
315
+ roomId
316
+ }),
317
+ emitEvent: dispatch
318
+ });
319
+ };
320
+ const createClient = (input)=>{
321
+ const client = createClientCore({
322
+ ast: input.ast
323
+ });
324
+ const register = (registry)=>{
325
+ const requestKey = input.requestKey ?? DEFAULT_REQUEST_KEY;
326
+ setClientRequest({
327
+ client,
328
+ registry,
329
+ requestKey
330
+ });
331
+ registry.onReceive((envelope, _ctx, next)=>{
332
+ client.emitEvent(buildClientEventEnvelope(envelope));
333
+ return next();
334
+ });
335
+ };
336
+ const moduleBase = {
337
+ name: input.name ?? 'client',
338
+ register
339
+ };
340
+ const moduleWithClient = Object.assign(moduleBase, client);
341
+ return moduleWithClient;
342
+ };
343
+ const createClientModule = (input)=>createClient(input);
344
+ exports.clientModule = __webpack_exports__.clientModule;
345
+ exports.createClient = __webpack_exports__.createClient;
346
+ exports.createClientModule = __webpack_exports__.createClientModule;
347
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
348
+ "clientModule",
349
+ "createClient",
350
+ "createClientModule"
351
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
352
+ Object.defineProperty(exports, '__esModule', {
353
+ value: true
354
+ });
@@ -0,0 +1,100 @@
1
+ import type { EventStatus, RuntimeEventContext, RuntimeModule } from '@livon/runtime';
2
+ export interface AstNode {
3
+ type: string;
4
+ name?: string;
5
+ doc?: Readonly<Record<string, unknown>>;
6
+ request?: string;
7
+ response?: string;
8
+ dependsOn?: string;
9
+ constraints?: Readonly<Record<string, unknown>>;
10
+ children?: readonly AstNode[];
11
+ }
12
+ export interface ClientRequest {
13
+ (event: string, payload: unknown): Promise<unknown>;
14
+ }
15
+ export interface ClientTransportConnect {
16
+ (): Promise<void>;
17
+ }
18
+ export interface ClientTransportClose {
19
+ (): void;
20
+ }
21
+ export interface ClientOptions {
22
+ ast: AstNode;
23
+ }
24
+ export interface ClientModuleInput {
25
+ ast: AstNode;
26
+ name?: string;
27
+ requestKey?: string;
28
+ }
29
+ export interface ClientModule extends RuntimeModule {
30
+ }
31
+ export interface ClientHandlerContext {
32
+ eventId: string;
33
+ event: string;
34
+ status: EventStatus;
35
+ room?: string;
36
+ metadata?: Readonly<Record<string, unknown>>;
37
+ context?: RuntimeEventContext;
38
+ }
39
+ export interface ClientSubscriptionHandler {
40
+ (payload: unknown, ctx: ClientHandlerContext): void;
41
+ }
42
+ export interface ClientEventEnvelope {
43
+ id: string;
44
+ event: string;
45
+ status: EventStatus;
46
+ payload?: unknown;
47
+ error?: unknown;
48
+ metadata?: Readonly<Record<string, unknown>>;
49
+ context?: RuntimeEventContext;
50
+ }
51
+ export interface ClientEventEmitter {
52
+ emitEvent: (envelope: ClientEventEnvelope) => void;
53
+ }
54
+ export interface ClientRequestSetter {
55
+ setRequest: (request: ClientRequest) => void;
56
+ }
57
+ export interface ClientModuleOptions {
58
+ name?: string;
59
+ requestKey?: string;
60
+ }
61
+ /**
62
+ * clientModule is part of the public LIVON API.
63
+ *
64
+ * @remarks
65
+ * Parameter and return types are defined in the TypeScript signature.
66
+ *
67
+ * @see https://live-input-vector-output-node.github.io/livon-ts/docs/packages/client
68
+ *
69
+ * @example
70
+ * const result = clientModule(undefined as never);
71
+ */
72
+ export declare const clientModule: (client: ClientEventEmitter, options?: ClientModuleOptions) => RuntimeModule;
73
+ /**
74
+ * createClient is part of the public LIVON API.
75
+ *
76
+ * @remarks
77
+ * Parameter and return types are defined in the TypeScript signature.
78
+ *
79
+ * @see https://live-input-vector-output-node.github.io/livon-ts/docs/packages/client
80
+ *
81
+ * @example
82
+ * const result = createClient(undefined as never);
83
+ */
84
+ export declare const createClient: (input: ClientModuleInput) => ClientModule & Record<string, unknown> & {
85
+ __register: (handlers: Record<string, ClientSubscriptionHandler>, roomId?: string) => void;
86
+ __toggle: (event: string, enabled: boolean, roomId?: string) => void;
87
+ emitEvent: (envelope: ClientEventEnvelope) => void;
88
+ };
89
+ /**
90
+ * createClientModule is part of the public LIVON API.
91
+ *
92
+ * @remarks
93
+ * Parameter and return types are defined in the TypeScript signature.
94
+ *
95
+ * @see https://live-input-vector-output-node.github.io/livon-ts/docs/packages/client
96
+ *
97
+ * @example
98
+ * const result = createClientModule(undefined as never);
99
+ */
100
+ export declare const createClientModule: (input: ClientModuleInput) => ClientModule & Record<string, unknown>;