@skipruntime/core 0.0.15 → 0.0.17

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.
package/dist/src/index.js CHANGED
@@ -7,7 +7,7 @@ import { deepFreeze, SkManaged, checkOrCloneParam, } from "../skiplang-json/inde
7
7
  import { sknative } from "../skiplang-std/index.js";
8
8
  import {} from "./api.js";
9
9
  import { SkipClassNameError, SkipError, SkipNonUniqueValueError, SkipResourceInstanceInUseError, SkipUnknownCollectionError, } from "./errors.js";
10
- import { ResourceBuilder, } from "./binding.js";
10
+ import {} from "./binding.js";
11
11
  export * from "./api.js";
12
12
  export * from "./errors.js";
13
13
  function instantiateUserObject(what, ctor, params) {
@@ -17,7 +17,93 @@ function instantiateUserObject(what, ctor, params) {
17
17
  if (!obj.constructor.name) {
18
18
  throw new SkipClassNameError(`${what} classes must be defined at top-level.`);
19
19
  }
20
- return obj;
20
+ return {
21
+ object: obj,
22
+ name: obj.constructor.name,
23
+ params: checkedParams,
24
+ };
25
+ }
26
+ export var LoadStatus;
27
+ (function (LoadStatus) {
28
+ LoadStatus[LoadStatus["Incompatible"] = 0] = "Incompatible";
29
+ LoadStatus[LoadStatus["Changed"] = 1] = "Changed";
30
+ LoadStatus[LoadStatus["Same"] = 2] = "Same";
31
+ })(LoadStatus || (LoadStatus = {}));
32
+ export class ServiceDefinition {
33
+ constructor(service, externals = new Map()) {
34
+ this.service = service;
35
+ this.externals = externals;
36
+ }
37
+ buildResource(name, parameters) {
38
+ const builder = this.service.resources[name];
39
+ if (!builder)
40
+ throw new Error(`Resource '${name}' not exist.`);
41
+ return new builder(parameters);
42
+ }
43
+ inputs() {
44
+ return this.service.initialData
45
+ ? Object.keys(this.service.initialData)
46
+ : [];
47
+ }
48
+ resources() {
49
+ return Object.keys(this.service.resources);
50
+ }
51
+ initialData(name) {
52
+ if (!this.service.initialData)
53
+ throw new Error(`No initial data defined.`);
54
+ const data = this.service.initialData[name];
55
+ if (!data)
56
+ throw new Error(`Initial data '${name}' not exist.`);
57
+ return data;
58
+ }
59
+ createGraph(inputCollections, context) {
60
+ return this.service.createGraph(inputCollections, context);
61
+ }
62
+ subscribe(external, writer, instance, resource, params) {
63
+ if (!this.service.externalServices)
64
+ throw new Error(`No external services defined.`);
65
+ const supplier = this.service.externalServices[external];
66
+ if (!supplier)
67
+ throw new Error(`External services '${external}' not exist.`);
68
+ this.externals.set(`${external}/${instance}`, supplier);
69
+ // Ensure notification is made outside the current context update
70
+ return new Promise((resolve, reject) => {
71
+ setTimeout(() => {
72
+ supplier
73
+ .subscribe(instance, resource, params, {
74
+ update: writer.update.bind(writer),
75
+ error: (_) => { },
76
+ })
77
+ .then(resolve)
78
+ .catch(reject);
79
+ }, 0);
80
+ });
81
+ }
82
+ unsubscribe(external, instance) {
83
+ if (!this.service.externalServices)
84
+ throw new Error(`No external services defined.`);
85
+ const supplier = this.externals.get(`${external}/${instance}`);
86
+ if (!supplier)
87
+ throw new Error(`External services '${external}/${instance}' not exist.`);
88
+ supplier.unsubscribe(instance);
89
+ this.externals.delete(`${external}/${instance}`);
90
+ }
91
+ async shutdown() {
92
+ const promises = [];
93
+ const uniqueServices = new Set(this.externals.values());
94
+ if (this.service.externalServices) {
95
+ for (const es of Object.values(this.service.externalServices)) {
96
+ uniqueServices.add(es);
97
+ }
98
+ }
99
+ for (const es of uniqueServices) {
100
+ promises.push(es.shutdown());
101
+ }
102
+ await Promise.all(promises);
103
+ }
104
+ derive(service) {
105
+ return new ServiceDefinition(service, new Map(this.externals));
106
+ }
21
107
  }
22
108
  class Handles {
23
109
  constructor() {
@@ -61,15 +147,6 @@ export class Stack {
61
147
  this.stack.pop();
62
148
  }
63
149
  }
64
- export class Refs {
65
- constructor(binding, skjson, handles, needGC, runWithGC) {
66
- this.binding = binding;
67
- this.skjson = skjson;
68
- this.handles = handles;
69
- this.needGC = needGC;
70
- this.runWithGC = runWithGC;
71
- }
72
- }
73
150
  class LazyCollectionImpl extends SkManaged {
74
151
  constructor(lazyCollection, refs) {
75
152
  super();
@@ -78,7 +155,9 @@ class LazyCollectionImpl extends SkManaged {
78
155
  Object.freeze(this);
79
156
  }
80
157
  getArray(key) {
81
- return this.refs.skjson.importJSON(this.refs.binding.SkipRuntime_LazyCollection__getArray(this.lazyCollection, this.refs.skjson.exportJSON(key)));
158
+ return this.refs
159
+ .json()
160
+ .importJSON(this.refs.binding.SkipRuntime_LazyCollection__getArray(this.lazyCollection, this.refs.json().exportJSON(key)));
82
161
  }
83
162
  getUnique(key, _default) {
84
163
  const values = this.getArray(key);
@@ -107,7 +186,9 @@ class EagerCollectionImpl extends SkManaged {
107
186
  Object.freeze(this);
108
187
  }
109
188
  getArray(key) {
110
- return this.refs.skjson.importJSON(this.refs.binding.SkipRuntime_Collection__getArray(this.collection, this.refs.skjson.exportJSON(key)));
189
+ return this.refs
190
+ .json()
191
+ .importJSON(this.refs.binding.SkipRuntime_Collection__getArray(this.collection, this.refs.json().exportJSON(key)));
111
192
  }
112
193
  getUnique(key, _default) {
113
194
  const values = this.getArray(key);
@@ -128,7 +209,7 @@ class EagerCollectionImpl extends SkManaged {
128
209
  return this.slices([start, end]);
129
210
  }
130
211
  slices(...ranges) {
131
- const skcollection = this.refs.binding.SkipRuntime_Collection__slice(this.collection, this.refs.skjson.exportJSON(ranges));
212
+ const skcollection = this.refs.binding.SkipRuntime_Collection__slice(this.collection, this.refs.json().exportJSON(ranges));
132
213
  return this.derive(skcollection);
133
214
  }
134
215
  take(limit) {
@@ -146,28 +227,30 @@ class EagerCollectionImpl extends SkManaged {
146
227
  const mapperObj = instantiateUserObject("Mapper", mapper, mapperParams);
147
228
  const reducerObj = instantiateUserObject("Reducer", reducer, reducerParams);
148
229
  const skmapper = this.refs.binding.SkipRuntime_createMapper(this.refs.handles.register(mapperObj));
149
- if (sknative in reducerObj && typeof reducerObj[sknative] == "string") {
150
- return this.derive(this.refs.binding.SkipRuntime_Collection__nativeMapReduce(this.collection, skmapper, reducerObj[sknative]));
230
+ if (sknative in reducerObj.object &&
231
+ typeof reducerObj.object[sknative] == "string") {
232
+ return this.derive(this.refs.binding.SkipRuntime_Collection__nativeMapReduce(this.collection, skmapper, reducerObj.object[sknative]));
151
233
  }
152
234
  else {
153
- const skreducer = this.refs.binding.SkipRuntime_createReducer(this.refs.handles.register(reducerObj), this.refs.skjson.exportJSON(reducerObj.initial));
235
+ const skreducer = this.refs.binding.SkipRuntime_createReducer(this.refs.handles.register(reducerObj));
154
236
  return this.derive(this.refs.binding.SkipRuntime_Collection__mapReduce(this.collection, skmapper, skreducer));
155
237
  }
156
238
  };
157
239
  }
158
240
  reduce(reducer, ...params) {
159
241
  const reducerObj = instantiateUserObject("Reducer", reducer, params);
160
- if (sknative in reducerObj && typeof reducerObj[sknative] == "string") {
161
- return this.derive(this.refs.binding.SkipRuntime_Collection__nativeReduce(this.collection, reducerObj[sknative]));
242
+ if (sknative in reducerObj.object &&
243
+ typeof reducerObj.object[sknative] == "string") {
244
+ return this.derive(this.refs.binding.SkipRuntime_Collection__nativeReduce(this.collection, reducerObj.object[sknative]));
162
245
  }
163
246
  else {
164
- const skreducer = this.refs.binding.SkipRuntime_createReducer(this.refs.handles.register(reducerObj), this.refs.skjson.exportJSON(reducerObj.initial));
247
+ const skreducer = this.refs.binding.SkipRuntime_createReducer(this.refs.handles.register(reducerObj));
165
248
  return this.derive(this.refs.binding.SkipRuntime_Collection__reduce(this.collection, skreducer));
166
249
  }
167
250
  }
168
251
  merge(...others) {
169
252
  const otherNames = others.map((other) => EagerCollectionImpl.getName(other));
170
- const mapped = this.refs.binding.SkipRuntime_Collection__merge(this.collection, this.refs.skjson.exportJSON(otherNames));
253
+ const mapped = this.refs.binding.SkipRuntime_Collection__merge(this.collection, this.refs.json().exportJSON(otherNames));
171
254
  return this.derive(mapped);
172
255
  }
173
256
  derive(collection) {
@@ -178,59 +261,61 @@ class EagerCollectionImpl extends SkManaged {
178
261
  }
179
262
  }
180
263
  class CollectionWriter {
181
- constructor(collection, refs) {
264
+ constructor(collection, refs, forkName) {
182
265
  this.collection = collection;
183
266
  this.refs = refs;
267
+ this.forkName = forkName;
184
268
  }
185
269
  async update(values, isInit) {
186
- await new Promise((resolve, reject) => {
187
- if (!this.refs.needGC()) {
188
- reject(new SkipError("CollectionWriter.update cannot be performed."));
189
- }
190
- const errorHdl = this.refs.runWithGC(() => {
191
- const exHdl = this.refs.handles.register({
192
- resolve,
193
- reject: (ex) => reject(ex),
194
- });
195
- return this.refs.binding.SkipRuntime_CollectionWriter__update(this.collection, this.refs.skjson.exportJSON(values), isInit, this.refs.binding.SkipRuntime_createExecutor(exHdl));
196
- });
197
- if (errorHdl)
198
- reject(this.refs.handles.deleteHandle(errorHdl));
199
- });
200
- }
201
- error(error) {
202
- if (!this.refs.needGC()) {
203
- throw new SkipError("CollectionWriter.update cannot be performed.");
270
+ this.refs.setFork(this.forkName);
271
+ const uuid = crypto.randomUUID();
272
+ const fork = this.fork(uuid);
273
+ try {
274
+ await fork.update_(values, isInit);
275
+ fork.merge();
276
+ }
277
+ catch (ex) {
278
+ fork.abortFork();
279
+ throw ex;
204
280
  }
205
- const errorHdl = this.refs.runWithGC(() => this.refs.binding.SkipRuntime_CollectionWriter__error(this.collection, this.refs.skjson.exportJSON(this.toJSONError(error))));
206
- if (errorHdl)
207
- throw this.refs.handles.deleteHandle(errorHdl);
208
281
  }
209
- initialized(error) {
282
+ update_(values, isInit) {
283
+ this.refs.setFork(this.getForkName());
210
284
  if (!this.refs.needGC()) {
211
285
  throw new SkipError("CollectionWriter.update cannot be performed.");
212
286
  }
213
- const errorHdl = this.refs.runWithGC(() => this.refs.binding.SkipRuntime_CollectionWriter__initialized(this.collection, this.refs.skjson.exportJSON(error ? this.toJSONError(error) : null)));
214
- if (errorHdl)
215
- throw this.refs.handles.deleteHandle(errorHdl);
216
- }
217
- toJSONError(error) {
218
- if (error instanceof Error)
219
- return error.message;
220
- if (typeof error == "number")
221
- return error;
222
- if (typeof error == "boolean")
223
- return error;
224
- if (typeof error == "string")
225
- return error;
226
- return JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));
287
+ return this.refs.runAsync(() => this.refs.binding.SkipRuntime_CollectionWriter__update(this.collection, this.refs.json().exportJSON(values), isInit));
288
+ }
289
+ fork(name) {
290
+ this.refs.setFork(this.forkName);
291
+ this.refs.fork(name);
292
+ return new CollectionWriter(this.collection, this.refs, name);
293
+ }
294
+ merge() {
295
+ if (!this.forkName)
296
+ throw new Error("Unable to merge fork on main.");
297
+ this.refs.setFork(this.forkName);
298
+ this.refs.merge();
299
+ }
300
+ abortFork() {
301
+ if (!this.forkName)
302
+ throw new Error("Unable to abord fork on main.");
303
+ this.refs.setFork(this.forkName);
304
+ this.refs.abortFork();
305
+ }
306
+ getForkName() {
307
+ const forkName = this.forkName;
308
+ if (!forkName)
309
+ return null;
310
+ if (!this.refs.runWithGC(() => this.refs.binding.SkipRuntime_Runtime__forkExists(forkName))) {
311
+ this.forkName = null;
312
+ }
313
+ return this.forkName;
227
314
  }
228
315
  }
229
- class ContextImpl extends SkManaged {
316
+ class ContextImpl {
230
317
  constructor(refs) {
231
- super();
232
318
  this.refs = refs;
233
- Object.freeze(this);
234
319
  }
235
320
  createLazyCollection(compute, ...params) {
236
321
  const computeObj = instantiateUserObject("LazyCompute", compute, params);
@@ -239,11 +324,12 @@ class ContextImpl extends SkManaged {
239
324
  return new LazyCollectionImpl(lazyCollection, this.refs);
240
325
  }
241
326
  useExternalResource(resource) {
242
- const collection = this.refs.binding.SkipRuntime_Context__useExternalResource(resource.service, resource.identifier, this.refs.skjson.exportJSON(resource.params ?? {}));
327
+ const collection = this.refs.binding.SkipRuntime_Context__useExternalResource(resource.service, resource.identifier, this.refs.json().exportJSON(resource.params ?? {}));
243
328
  return new EagerCollectionImpl(collection, this.refs);
244
329
  }
245
330
  jsonExtract(value, pattern) {
246
- return this.refs.skjson.importJSON(this.refs.binding.SkipRuntime_Context__jsonExtract(this.refs.skjson.exportJSON(value), pattern));
331
+ const skjson = this.refs.json();
332
+ return skjson.importJSON(this.refs.binding.SkipRuntime_Context__jsonExtract(skjson.exportJSON(value), pattern));
247
333
  }
248
334
  }
249
335
  export class ServiceInstanceFactory {
@@ -259,27 +345,21 @@ export class ServiceInstanceFactory {
259
345
  * and operations to manage subscriptions and the service itself.
260
346
  */
261
347
  export class ServiceInstance {
262
- constructor(refs) {
348
+ constructor(refs, forkName, definition) {
263
349
  this.refs = refs;
350
+ this.forkName = forkName;
351
+ this.definition = definition;
264
352
  }
265
353
  /**
266
354
  * Instantiate a resource with some parameters and client session authentication token
267
355
  * @param identifier - The resource instance identifier
268
356
  * @param resource - A resource name, which must correspond to a key in this `SkipService`'s `resources` field
269
357
  * @param params - Resource parameters, which will be passed to the resource constructor specified in this `SkipService`'s `resources` field
358
+ * @returns The resulting promise
270
359
  */
271
360
  instantiateResource(identifier, resource, params) {
272
- return new Promise((resolve, reject) => {
273
- const errorHdl = this.refs.runWithGC(() => {
274
- const exHdl = this.refs.handles.register({
275
- resolve,
276
- reject: (ex) => reject(ex),
277
- });
278
- return this.refs.binding.SkipRuntime_Runtime__createResource(identifier, resource, this.refs.skjson.exportJSON(params), this.refs.binding.SkipRuntime_createExecutor(exHdl));
279
- });
280
- if (errorHdl)
281
- reject(this.refs.handles.deleteHandle(errorHdl));
282
- });
361
+ this.refs.setFork(this.forkName);
362
+ return this.refs.runAsync(() => this.refs.binding.SkipRuntime_Runtime__createResource(identifier, resource, this.refs.json().exportJSON(params)));
283
363
  }
284
364
  /**
285
365
  * Creates if not exists and get all current values of specified resource
@@ -291,15 +371,15 @@ export class ServiceInstance {
291
371
  const uuid = crypto.randomUUID();
292
372
  await this.instantiateResource(uuid, resource, params);
293
373
  try {
374
+ this.refs.setFork(this.forkName);
294
375
  const result = this.refs.runWithGC(() => {
295
- return this.refs.skjson.importJSON(this.refs.binding.SkipRuntime_Runtime__getAll(resource, this.refs.skjson.exportJSON(params)), true);
376
+ return this.refs
377
+ .json()
378
+ .importJSON(this.refs.binding.SkipRuntime_Runtime__getAll(resource, this.refs.json().exportJSON(params)), true);
296
379
  });
297
380
  if (typeof result == "number")
298
381
  throw this.refs.handles.deleteHandle(result);
299
- const info = result;
300
- if (info.errors.length > 0)
301
- throw new SkipError(JSON.stringify(info.errors));
302
- return info.payload;
382
+ return result;
303
383
  }
304
384
  finally {
305
385
  this.closeResourceInstance(uuid);
@@ -316,15 +396,14 @@ export class ServiceInstance {
316
396
  const uuid = crypto.randomUUID();
317
397
  await this.instantiateResource(uuid, resource, params);
318
398
  try {
399
+ this.refs.setFork(this.forkName);
400
+ const skjson = this.refs.json();
319
401
  const result = this.refs.runWithGC(() => {
320
- return this.refs.skjson.importJSON(this.refs.binding.SkipRuntime_Runtime__getForKey(resource, this.refs.skjson.exportJSON(params), this.refs.skjson.exportJSON(key)), true);
402
+ return skjson.importJSON(this.refs.binding.SkipRuntime_Runtime__getForKey(resource, skjson.exportJSON(params), skjson.exportJSON(key)), true);
321
403
  });
322
404
  if (typeof result == "number")
323
405
  throw this.refs.handles.deleteHandle(result);
324
- const info = result;
325
- if (info.errors.length > 0)
326
- throw new SkipError(JSON.stringify(info.errors));
327
- return info.payload;
406
+ return result;
328
407
  }
329
408
  finally {
330
409
  this.closeResourceInstance(uuid);
@@ -335,6 +414,7 @@ export class ServiceInstance {
335
414
  * @param resourceInstanceId - The resource identifier
336
415
  */
337
416
  closeResourceInstance(resourceInstanceId) {
417
+ this.refs.setFork(this.forkName);
338
418
  const errorHdl = this.refs.runWithGC(() => {
339
419
  return this.refs.binding.SkipRuntime_Runtime__closeResource(resourceInstanceId);
340
420
  });
@@ -352,6 +432,7 @@ export class ServiceInstance {
352
432
  * @returns A subscription identifier
353
433
  */
354
434
  subscribe(resourceInstanceId, notifier, watermark) {
435
+ this.refs.setFork(this.forkName);
355
436
  const session = this.refs.runWithGC(() => {
356
437
  const sknotifier = this.refs.binding.SkipRuntime_createNotifier(this.refs.handles.register(notifier));
357
438
  return this.refs.binding.SkipRuntime_Runtime__subscribe(resourceInstanceId, sknotifier, watermark ?? null);
@@ -365,13 +446,14 @@ export class ServiceInstance {
365
446
  else if (session < 0n) {
366
447
  throw this.refs.handles.deleteHandle(Number(-session));
367
448
  }
368
- return session;
449
+ return resourceInstanceId;
369
450
  }
370
451
  /**
371
452
  * Terminate a client's subscription to a reactive resource instance
372
453
  * @param id - The subscription identifier returned by a call to `subscribe`
373
454
  */
374
455
  unsubscribe(id) {
456
+ this.refs.setFork(this.forkName);
375
457
  const errorHdl = this.refs.runWithGC(() => {
376
458
  return this.refs.binding.SkipRuntime_Runtime__unsubscribe(id);
377
459
  });
@@ -384,18 +466,34 @@ export class ServiceInstance {
384
466
  * @param collection - the name of the input collection to update
385
467
  * @param entries - entries to update in the collection.
386
468
  */
387
- update(collection, entries) {
388
- return new Promise((resolve, reject) => {
389
- const errorHdl = this.refs.runWithGC(() => {
390
- const exHdl = this.refs.handles.register({
391
- resolve,
392
- reject: (ex) => reject(ex),
393
- });
394
- return this.refs.binding.SkipRuntime_Runtime__update(collection, this.refs.skjson.exportJSON(entries), this.refs.binding.SkipRuntime_createExecutor(exHdl));
395
- });
396
- if (errorHdl)
397
- reject(this.refs.handles.deleteHandle(errorHdl));
469
+ async update(collection, entries) {
470
+ this.refs.setFork(this.forkName);
471
+ const uuid = crypto.randomUUID();
472
+ const fork = this.fork(uuid);
473
+ try {
474
+ await fork.update_(collection, entries);
475
+ fork.merge([]);
476
+ }
477
+ catch (ex) {
478
+ fork.abortFork();
479
+ throw ex;
480
+ }
481
+ }
482
+ async update_(collection, entries) {
483
+ this.refs.setFork(this.forkName);
484
+ const result = this.refs.runWithGC(() => {
485
+ const json = this.refs.json();
486
+ return json.importJSON(this.refs.binding.SkipRuntime_Runtime__update(collection, this.refs.json().exportJSON(entries)), true);
398
487
  });
488
+ if (Array.isArray(result)) {
489
+ const handles = result;
490
+ const promises = handles.map((h) => this.refs.handles.deleteHandle(h));
491
+ await Promise.all(promises);
492
+ }
493
+ else {
494
+ const errorHdl = result;
495
+ throw this.refs.handles.deleteHandle(errorHdl);
496
+ }
399
497
  }
400
498
  /**
401
499
  * Close all resources and shut down the service.
@@ -403,19 +501,95 @@ export class ServiceInstance {
403
501
  * @returns The promise of externals services shutdowns
404
502
  */
405
503
  close() {
504
+ this.refs.setFork(this.forkName);
406
505
  const result = this.refs.runWithGC(() => {
407
- return this.refs.skjson.importJSON(this.refs.binding.SkipRuntime_closeService(), true);
506
+ return this.refs.binding.SkipRuntime_closeService();
507
+ });
508
+ if (result >= 0) {
509
+ return this.refs.handles.deleteHandle(result);
510
+ }
511
+ else {
512
+ const errorHdl = -result;
513
+ return Promise.reject(this.refs.handles.deleteHandle(errorHdl));
514
+ }
515
+ }
516
+ async reload(service, changes) {
517
+ if (this.forkName) {
518
+ throw new SkipError("Reload cannot be called in transaction.");
519
+ }
520
+ const definition = this.definition.derive(service);
521
+ this.refs.setFork(this.forkName);
522
+ const uuid = crypto.randomUUID();
523
+ const fork = this.fork(uuid);
524
+ let merged = false;
525
+ try {
526
+ const streamsToClose = await fork._reload(definition, changes);
527
+ fork.merge(streamsToClose);
528
+ merged = true;
529
+ this.closeResourceStreams(streamsToClose);
530
+ this.definition = definition;
531
+ }
532
+ catch (ex) {
533
+ console.error(ex);
534
+ if (!merged)
535
+ fork.abortFork();
536
+ throw ex;
537
+ }
538
+ }
539
+ async _reload(definition, changes) {
540
+ this.refs.setFork(this.forkName);
541
+ const result = this.refs.runWithGC(() => {
542
+ this.refs.changes = this.refs.handles.register(changes);
543
+ const skservicehHdl = this.refs.handles.register(definition);
544
+ const skservice = this.refs.binding.SkipRuntime_createService(skservicehHdl);
545
+ const res = this.refs.binding.SkipRuntime_Runtime__reload(skservice);
546
+ this.refs.handles.deleteHandle(this.refs.changes);
547
+ this.refs.changes = null;
548
+ return this.refs.json().importJSON(res, true);
408
549
  });
409
550
  if (Array.isArray(result)) {
410
- const handles = result;
551
+ const [handles, res] = result;
411
552
  const promises = handles.map((h) => this.refs.handles.deleteHandle(h));
412
- return Promise.all(promises);
553
+ await Promise.all(promises);
554
+ return res;
413
555
  }
414
556
  else {
415
557
  const errorHdl = result;
416
- return Promise.reject(this.refs.handles.deleteHandle(errorHdl));
558
+ throw this.refs.handles.deleteHandle(errorHdl);
417
559
  }
418
560
  }
561
+ /**
562
+ * Fork the service with current specified name.
563
+ * @param name - the name of the fork.
564
+ * @returns The forked ServiceInstance
565
+ */
566
+ fork(name) {
567
+ if (this.forkName)
568
+ throw new Error(`Unable to fork ${this.forkName}.`);
569
+ this.refs.setFork(this.forkName);
570
+ this.refs.fork(name);
571
+ return new ServiceInstance(this.refs, name, this.definition);
572
+ }
573
+ merge(ignore) {
574
+ if (!this.forkName)
575
+ throw new Error("Unable to merge fork on main.");
576
+ this.refs.setFork(this.forkName);
577
+ this.refs.merge(ignore);
578
+ }
579
+ abortFork() {
580
+ if (!this.forkName)
581
+ throw new Error("Unable to abord fork on main.");
582
+ this.refs.setFork(this.forkName);
583
+ this.refs.abortFork();
584
+ }
585
+ closeResourceStreams(streams) {
586
+ this.refs.setFork(this.forkName);
587
+ const errorHdl = this.refs.runWithGC(() => {
588
+ return this.refs.binding.SkipRuntime_Runtime__closeResourceStreams(this.refs.json().exportJSON(streams));
589
+ });
590
+ if (errorHdl)
591
+ throw this.refs.handles.deleteHandle(errorHdl);
592
+ }
419
593
  }
420
594
  class ValuesImpl {
421
595
  constructor(skjson, binding, pointer) {
@@ -486,6 +660,8 @@ export class ToBinding {
486
660
  this.getError = getError;
487
661
  this.stack = new Stack();
488
662
  this.handles = new Handles();
663
+ this.forkName = null;
664
+ this.changes = null;
489
665
  }
490
666
  register(v) {
491
667
  return this.handles.register(v);
@@ -505,14 +681,33 @@ export class ToBinding {
505
681
  SkipRuntime_getContext() {
506
682
  return this.stack.get();
507
683
  }
684
+ SkipRuntime_getFork() {
685
+ return this.forkName;
686
+ }
687
+ SkipRuntime_getChangeManager() {
688
+ return this.changes ?? 0;
689
+ }
690
+ setFork(name) {
691
+ this.forkName = name;
692
+ }
508
693
  // Mapper
509
694
  SkipRuntime_Mapper__mapEntry(skmapper, key, values) {
510
695
  const skjson = this.getJsonConverter();
511
696
  const mapper = this.handles.get(skmapper);
512
- const context = new ContextImpl(this.refs());
513
- const result = mapper.mapEntry(skjson.importJSON(key), new ValuesImpl(skjson, this.binding, values), context);
697
+ const context = new ContextImpl(this);
698
+ const result = mapper.object.mapEntry(skjson.importJSON(key), new ValuesImpl(skjson, this.binding, values), context);
514
699
  return skjson.exportJSON(Array.from(result));
515
700
  }
701
+ SkipRuntime_Mapper__getInfo(skmapper) {
702
+ return this.getInfo(skmapper);
703
+ }
704
+ SkipRuntime_Mapper__isEquals(mapper, other) {
705
+ const object = this.handles.get(mapper);
706
+ if (this.getChanges()?.needMapperReload(object.name)) {
707
+ return 0;
708
+ }
709
+ return this.isEquals(mapper, other);
710
+ }
516
711
  SkipRuntime_deleteMapper(mapper) {
517
712
  this.handles.deleteHandle(mapper);
518
713
  }
@@ -520,9 +715,20 @@ export class ToBinding {
520
715
  SkipRuntime_LazyCompute__compute(sklazyCompute, self, skkey) {
521
716
  const skjson = this.getJsonConverter();
522
717
  const lazyCompute = this.handles.get(sklazyCompute);
523
- const result = lazyCompute.compute(new LazyCollectionImpl(self, this.refs()), skjson.importJSON(skkey));
718
+ const context = new ContextImpl(this);
719
+ const result = lazyCompute.object.compute(new LazyCollectionImpl(self, this), skjson.importJSON(skkey), context);
524
720
  return skjson.exportJSON(Array.from(result));
525
721
  }
722
+ SkipRuntime_LazyCompute__getInfo(lazyCompute) {
723
+ return this.getInfo(lazyCompute);
724
+ }
725
+ SkipRuntime_LazyCompute__isEquals(lazyCompute, other) {
726
+ const object = this.handles.get(lazyCompute);
727
+ if (this.getChanges()?.needLazyComputeReload(object.name)) {
728
+ return 0;
729
+ }
730
+ return this.isEquals(lazyCompute, other);
731
+ }
526
732
  SkipRuntime_deleteLazyCompute(lazyCompute) {
527
733
  this.handles.deleteHandle(lazyCompute);
528
734
  }
@@ -532,46 +738,83 @@ export class ToBinding {
532
738
  const resource = this.handles.get(skresource);
533
739
  const collections = {};
534
740
  const keysIds = skjson.importJSON(skcollections);
535
- const refs = this.refs();
536
741
  for (const [key, name] of Object.entries(keysIds)) {
537
- collections[key] = new EagerCollectionImpl(name, refs);
742
+ collections[key] = new EagerCollectionImpl(name, this);
538
743
  }
539
- const collection = resource.instantiate(collections, new ContextImpl(refs));
744
+ const collection = resource.instantiate(collections, new ContextImpl(this));
540
745
  return EagerCollectionImpl.getName(collection);
541
746
  }
542
747
  SkipRuntime_deleteResource(resource) {
543
748
  this.handles.deleteHandle(resource);
544
749
  }
545
- // ResourceBuilder
546
- SkipRuntime_ResourceBuilder__build(skbuilder, skparams) {
547
- const skjson = this.getJsonConverter();
548
- const builder = this.handles.get(skbuilder);
549
- const resource = builder.build(skjson.importJSON(skparams));
550
- return this.binding.SkipRuntime_createResource(this.handles.register(resource));
551
- }
552
- SkipRuntime_deleteResourceBuilder(builder) {
553
- this.handles.deleteHandle(builder);
554
- }
555
- // Service
556
- SkipRuntime_Service__createGraph(skservice, skcollections) {
750
+ // ServiceDefinition
751
+ SkipRuntime_ServiceDefinition__createGraph(skservice, skcollections) {
557
752
  const skjson = this.getJsonConverter();
558
753
  const service = this.handles.get(skservice);
559
754
  const collections = {};
560
755
  const keysIds = skjson.importJSON(skcollections);
561
- const refs = this.refs();
562
756
  for (const [key, name] of Object.entries(keysIds)) {
563
- collections[key] = new EagerCollectionImpl(name, refs);
757
+ collections[key] = new EagerCollectionImpl(name, this);
564
758
  }
565
- const result = service.createGraph(collections, new ContextImpl(refs));
759
+ const result = service.createGraph(collections, new ContextImpl(this));
566
760
  const collectionsNames = {};
567
761
  for (const [name, collection] of Object.entries(result)) {
568
762
  collectionsNames[name] = EagerCollectionImpl.getName(collection);
569
763
  }
570
764
  return skjson.exportJSON(collectionsNames);
571
765
  }
766
+ SkipRuntime_ServiceDefinition__inputs(skservice) {
767
+ const skjson = this.getJsonConverter();
768
+ const service = this.handles.get(skservice);
769
+ return skjson.exportJSON(service.inputs());
770
+ }
771
+ SkipRuntime_ServiceDefinition__resources(skservice) {
772
+ const skjson = this.getJsonConverter();
773
+ const service = this.handles.get(skservice);
774
+ return skjson.exportJSON(service.resources());
775
+ }
776
+ SkipRuntime_ServiceDefinition__initialData(skservice, name) {
777
+ const skjson = this.getJsonConverter();
778
+ const service = this.handles.get(skservice);
779
+ return skjson.exportJSON(service.initialData(name));
780
+ }
781
+ SkipRuntime_ServiceDefinition__buildResource(skservice, name, skparams) {
782
+ const skjson = this.getJsonConverter();
783
+ const service = this.handles.get(skservice);
784
+ const resource = service.buildResource(name, skjson.importJSON(skparams));
785
+ return this.binding.SkipRuntime_createResource(this.handles.register(resource));
786
+ }
787
+ SkipRuntime_ServiceDefinition__subscribe(skservice, external, writerId, instance, resource, skparams) {
788
+ const skjson = this.getJsonConverter();
789
+ const service = this.handles.get(skservice);
790
+ const writer = new CollectionWriter(writerId, this, this.forkName);
791
+ const params = skjson.importJSON(skparams, true);
792
+ return this.handles.register(service.subscribe(external, writer, instance, resource, params));
793
+ }
794
+ SkipRuntime_ServiceDefinition__unsubscribe(skservice, external, instance) {
795
+ const service = this.handles.get(skservice);
796
+ service.unsubscribe(external, instance);
797
+ }
798
+ SkipRuntime_ServiceDefinition__shutdown(skservice) {
799
+ const service = this.handles.get(skservice);
800
+ return this.handles.register(service.shutdown());
801
+ }
572
802
  SkipRuntime_deleteService(service) {
573
803
  this.handles.deleteHandle(service);
574
804
  }
805
+ // Change manager
806
+ SkipRuntime_ChangeManager__needInputReload(skmanager, name) {
807
+ const manager = this.handles.get(skmanager);
808
+ return manager.needInputReload(name) ? 1 : 0;
809
+ }
810
+ SkipRuntime_ChangeManager__needResourceReload(skmanager, name) {
811
+ const manager = this.handles.get(skmanager);
812
+ return manager.needResourceReload(name);
813
+ }
814
+ SkipRuntime_ChangeManager__needExternalServiceReload(skmanager, name, resource) {
815
+ const manager = this.handles.get(skmanager);
816
+ return manager.needExternalServiceReload(name, resource) ? 1 : 0;
817
+ }
575
818
  // Notifier
576
819
  SkipRuntime_Notifier__subscribed(sknotifier) {
577
820
  const notifier = this.handles.get(sknotifier);
@@ -596,88 +839,57 @@ export class ToBinding {
596
839
  this.handles.deleteHandle(notifier);
597
840
  }
598
841
  // Reducer
599
- SkipRuntime_Reducer__add(skreducer, skacc, skvalue) {
842
+ SkipRuntime_Reducer__init(skreducer) {
600
843
  const skjson = this.getJsonConverter();
601
844
  const reducer = this.handles.get(skreducer);
602
- return skjson.exportJSON(reducer.add(skacc ? skjson.importJSON(skacc) : null, skjson.importJSON(skvalue)));
845
+ return skjson.exportJSON(reducer.object.initial);
603
846
  }
604
- SkipRuntime_Reducer__remove(skreducer, skacc, skvalue) {
847
+ SkipRuntime_Reducer__add(skreducer, skacc, skvalue) {
605
848
  const skjson = this.getJsonConverter();
606
849
  const reducer = this.handles.get(skreducer);
607
- return skjson.exportJSON(reducer.remove(skjson.importJSON(skacc), skjson.importJSON(skvalue)));
850
+ return skjson.exportJSON(reducer.object.add(skacc ? skjson.importJSON(skacc) : null, skjson.importJSON(skvalue)));
608
851
  }
609
- SkipRuntime_deleteReducer(reducer) {
610
- this.handles.deleteHandle(reducer);
611
- }
612
- // ExternalService
613
- SkipRuntime_ExternalService__subscribe(sksupplier, writerId, instance, resource, skparams) {
852
+ SkipRuntime_Reducer__remove(skreducer, skacc, skvalue) {
614
853
  const skjson = this.getJsonConverter();
615
- const supplier = this.handles.get(sksupplier);
616
- const writer = new CollectionWriter(writerId, this.refs());
617
- const params = skjson.importJSON(skparams, true);
618
- // Ensure notification is made outside the current context update
619
- setTimeout(() => {
620
- supplier
621
- .subscribe(instance, resource, params, {
622
- update: writer.update.bind(writer),
623
- error: writer.error.bind(writer),
624
- })
625
- .then(() => writer.initialized())
626
- .catch((e) => writer.initialized(e instanceof Error
627
- ? e.message
628
- : JSON.stringify(e, Object.getOwnPropertyNames(e))));
629
- }, 0);
630
- }
631
- SkipRuntime_ExternalService__unsubscribe(sksupplier, instance) {
632
- const supplier = this.handles.get(sksupplier);
633
- supplier.unsubscribe(instance);
634
- }
635
- SkipRuntime_ExternalService__shutdown(sksupplier) {
636
- const supplier = this.handles.get(sksupplier);
637
- return this.handles.register(supplier.shutdown());
854
+ const reducer = this.handles.get(skreducer);
855
+ return skjson.exportJSON(reducer.object.remove(skjson.importJSON(skacc), skjson.importJSON(skvalue)));
638
856
  }
639
- SkipRuntime_deleteExternalService(supplier) {
640
- this.handles.deleteHandle(supplier);
857
+ SkipRuntime_Reducer__isEquals(reducer, other) {
858
+ const object = this.handles.get(reducer);
859
+ if (this.getChanges()?.needReducerReload(object.name)) {
860
+ return 0;
861
+ }
862
+ return this.isEquals(reducer, other);
641
863
  }
642
- // Executor
643
- SkipRuntime_Executor__resolve(skexecutor) {
644
- const checker = this.handles.get(skexecutor);
645
- checker.resolve();
864
+ SkipRuntime_Reducer__getInfo(reducer) {
865
+ return this.getInfo(reducer);
646
866
  }
647
- SkipRuntime_Executor__reject(skexecutor, error) {
648
- const checker = this.handles.get(skexecutor);
649
- checker.reject(this.handles.deleteHandle(error));
867
+ SkipRuntime_deleteReducer(reducer) {
868
+ this.handles.deleteHandle(reducer);
650
869
  }
651
- SkipRuntime_deleteExecutor(executor) {
652
- this.handles.deleteHandle(executor);
870
+ async initService(service) {
871
+ this.setFork(null);
872
+ const uuid = crypto.randomUUID();
873
+ this.fork(uuid);
874
+ try {
875
+ this.setFork(uuid);
876
+ const definition = new ServiceDefinition(service);
877
+ await this.initService_(definition);
878
+ this.setFork(uuid);
879
+ this.merge();
880
+ return new ServiceInstance(this, null, definition);
881
+ }
882
+ catch (ex) {
883
+ this.setFork(uuid);
884
+ this.abortFork();
885
+ throw ex;
886
+ }
653
887
  }
654
- initService(service) {
655
- return new Promise((resolve, reject) => {
656
- const refs = this.refs();
657
- const errorHdl = refs.runWithGC(() => {
658
- const skExternalServices = refs.binding.SkipRuntime_ExternalServiceMap__create();
659
- if (service.externalServices) {
660
- for (const [name, remote] of Object.entries(service.externalServices)) {
661
- const skremote = refs.binding.SkipRuntime_createExternalService(refs.handles.register(remote));
662
- refs.binding.SkipRuntime_ExternalServiceMap__add(skExternalServices, name, skremote);
663
- }
664
- }
665
- const skresources = refs.binding.SkipRuntime_ResourceBuilderMap__create();
666
- for (const [name, builder] of Object.entries(service.resources)) {
667
- const skbuilder = refs.binding.SkipRuntime_createResourceBuilder(refs.handles.register(new ResourceBuilder(builder)));
668
- refs.binding.SkipRuntime_ResourceBuilderMap__add(skresources, name, skbuilder);
669
- }
670
- const skservice = refs.binding.SkipRuntime_createService(refs.handles.register(service), refs.skjson.exportJSON(service.initialData ?? {}), skresources, skExternalServices);
671
- const exHdl = refs.handles.register({
672
- resolve: () => {
673
- resolve(new ServiceInstance(refs));
674
- },
675
- reject: (ex) => reject(ex),
676
- });
677
- return refs.binding.SkipRuntime_initService(skservice, refs.binding.SkipRuntime_createExecutor(exHdl));
678
- });
679
- if (errorHdl)
680
- reject(refs.handles.deleteHandle(errorHdl));
888
+ initService_(definition) {
889
+ return this.runAsync(() => {
890
+ const skservicehHdl = this.handles.register(definition);
891
+ const skservice = this.binding.SkipRuntime_createService(skservicehHdl);
892
+ return this.binding.SkipRuntime_initService(skservice);
681
893
  });
682
894
  }
683
895
  //
@@ -690,8 +902,132 @@ export class ToBinding {
690
902
  needGC() {
691
903
  return this.SkipRuntime_getContext() == null;
692
904
  }
693
- refs() {
694
- return new Refs(this.binding, this.getConverter(), this.handles, this.needGC.bind(this), this.runWithGC);
905
+ json() {
906
+ return this.getJsonConverter();
907
+ }
908
+ fork(name) {
909
+ const errorHdl = this.runWithGC(() => this.binding.SkipRuntime_Runtime__fork(name));
910
+ if (errorHdl)
911
+ throw this.handles.deleteHandle(errorHdl);
912
+ }
913
+ merge(ignore = []) {
914
+ const errorHdl = this.runWithGC(() => this.binding.SkipRuntime_Runtime__merge(this.json().exportJSON(ignore)));
915
+ if (errorHdl)
916
+ throw this.handles.deleteHandle(errorHdl);
917
+ }
918
+ abortFork() {
919
+ const errorHdl = this.runWithGC(() => this.binding.SkipRuntime_Runtime__abortFork());
920
+ if (errorHdl)
921
+ throw this.handles.deleteHandle(errorHdl);
922
+ }
923
+ async runAsync(fn) {
924
+ const result = this.runWithGC(() => {
925
+ return this.json().importJSON(fn(), true);
926
+ });
927
+ if (Array.isArray(result)) {
928
+ const handles = result;
929
+ const promises = handles.map((h) => this.handles.deleteHandle(h));
930
+ await Promise.all(promises);
931
+ }
932
+ else {
933
+ const errorHdl = result;
934
+ throw this.handles.deleteHandle(errorHdl);
935
+ }
936
+ }
937
+ deepEquals(a, b) {
938
+ // Same reference or both NaN
939
+ if (a === b)
940
+ return true;
941
+ if (a !== a && b !== b)
942
+ return true; // NaN check
943
+ // Different types or one is null
944
+ if (typeof a !== typeof b || a === null || b === null)
945
+ return false;
946
+ // Primitives already checked by ===
947
+ if (typeof a !== "object" || typeof b !== "object")
948
+ return false;
949
+ // Arrays
950
+ if (Array.isArray(a)) {
951
+ if (!Array.isArray(b) || a.length !== b.length)
952
+ return false;
953
+ for (let i = 0; i < a.length; i++) {
954
+ if (!this.deepEquals(a[i], b[i]))
955
+ return false;
956
+ }
957
+ return true;
958
+ }
959
+ // Different array status
960
+ if (Array.isArray(b))
961
+ return false;
962
+ // Objects
963
+ const keysA = Object.keys(a);
964
+ const keysB = Object.keys(b);
965
+ if (keysA.length !== keysB.length)
966
+ return false;
967
+ for (const key of keysA) {
968
+ if (!Object.prototype.hasOwnProperty.call(b, key) ||
969
+ !this.deepEquals(a[key], b[key]))
970
+ return false;
971
+ }
972
+ return true;
973
+ }
974
+ getInfo(skmapper) {
975
+ const skjson = this.getJsonConverter();
976
+ const object = this.handles.get(skmapper);
977
+ const name = object.name;
978
+ const parameters = object.params.map((v) => {
979
+ if (v instanceof EagerCollectionImpl) {
980
+ return {
981
+ type: "collection",
982
+ value: v.collection,
983
+ };
984
+ }
985
+ if (v instanceof LazyCollectionImpl) {
986
+ return {
987
+ type: "collection",
988
+ value: v.lazyCollection,
989
+ };
990
+ }
991
+ return { type: "data", value: v };
992
+ });
993
+ return skjson.exportJSON({ name, parameters });
994
+ }
995
+ isEquals(mapper, other) {
996
+ const object = this.handles.get(mapper);
997
+ const oobject = this.handles.get(other);
998
+ if (object.object.constructor != oobject.object.constructor) {
999
+ return 0;
1000
+ }
1001
+ if (object.params.length != oobject.params.length)
1002
+ return 0;
1003
+ for (const [i, param] of object.params.entries()) {
1004
+ const oparam = oobject.params[i];
1005
+ if (param instanceof EagerCollectionImpl) {
1006
+ if (oparam instanceof EagerCollectionImpl) {
1007
+ if (param.collection != oparam.collection)
1008
+ return 0;
1009
+ }
1010
+ else {
1011
+ return 0;
1012
+ }
1013
+ }
1014
+ else if (param instanceof LazyCollectionImpl) {
1015
+ if (oparam instanceof LazyCollectionImpl) {
1016
+ if (param.lazyCollection != oparam.lazyCollection)
1017
+ return 0;
1018
+ }
1019
+ else {
1020
+ return 0;
1021
+ }
1022
+ }
1023
+ else if (!this.deepEquals(param, oparam)) {
1024
+ return 0;
1025
+ }
1026
+ }
1027
+ return 1;
1028
+ }
1029
+ getChanges() {
1030
+ return this.changes ? this.handles.get(this.changes) : null;
695
1031
  }
696
1032
  }
697
1033
  //# sourceMappingURL=index.js.map