@skipruntime/core 0.0.16 → 0.0.18
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/api.d.ts +2 -2
- package/dist/src/api.d.ts.map +1 -1
- package/dist/src/binding.d.ts +20 -25
- package/dist/src/binding.d.ts.map +1 -1
- package/dist/src/binding.js.map +1 -1
- package/dist/src/index.d.ts +109 -35
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +529 -194
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +0 -2
- package/dist/src/internal.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api.ts +2 -2
- package/src/binding.ts +28 -63
- package/src/index.ts +714 -317
- package/src/internal.ts +0 -3
package/src/index.ts
CHANGED
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
type Context,
|
|
27
27
|
type EagerCollection,
|
|
28
28
|
type Entry,
|
|
29
|
-
type ExternalService,
|
|
30
29
|
type LazyCollection,
|
|
31
30
|
type LazyCompute,
|
|
32
31
|
type Mapper,
|
|
@@ -37,6 +36,7 @@ import {
|
|
|
37
36
|
type Resource,
|
|
38
37
|
type SkipService,
|
|
39
38
|
type Watermark,
|
|
39
|
+
type ExternalService,
|
|
40
40
|
} from "./api.js";
|
|
41
41
|
|
|
42
42
|
import {
|
|
@@ -46,25 +46,26 @@ import {
|
|
|
46
46
|
SkipResourceInstanceInUseError,
|
|
47
47
|
SkipUnknownCollectionError,
|
|
48
48
|
} from "./errors.js";
|
|
49
|
-
import {
|
|
50
|
-
ResourceBuilder,
|
|
51
|
-
type Notifier,
|
|
52
|
-
type Executor,
|
|
53
|
-
type Handle,
|
|
54
|
-
type FromBinding,
|
|
55
|
-
} from "./binding.js";
|
|
49
|
+
import { type Notifier, type Handle, type FromBinding } from "./binding.js";
|
|
56
50
|
|
|
57
51
|
export * from "./api.js";
|
|
58
52
|
export * from "./errors.js";
|
|
59
53
|
|
|
60
54
|
export type JSONMapper = Mapper<Json, Json, Json, Json>;
|
|
61
55
|
export type JSONLazyCompute = LazyCompute<Json, Json>;
|
|
56
|
+
export type JSONOperator = JSONMapper | JSONLazyCompute | Reducer<Json, Json>;
|
|
57
|
+
|
|
58
|
+
export type HandlerInfo<P> = {
|
|
59
|
+
object: P;
|
|
60
|
+
name: string;
|
|
61
|
+
params: DepSafe[];
|
|
62
|
+
};
|
|
62
63
|
|
|
63
64
|
function instantiateUserObject<Params extends DepSafe[], Result extends object>(
|
|
64
65
|
what: string,
|
|
65
66
|
ctor: new (...params: Params) => Result,
|
|
66
67
|
params: Params,
|
|
67
|
-
): Result {
|
|
68
|
+
): HandlerInfo<Result> {
|
|
68
69
|
const checkedParams = params.map(checkOrCloneParam) as Params;
|
|
69
70
|
const obj = new ctor(...checkedParams);
|
|
70
71
|
Object.freeze(obj);
|
|
@@ -73,7 +74,118 @@ function instantiateUserObject<Params extends DepSafe[], Result extends object>(
|
|
|
73
74
|
`${what} classes must be defined at top-level.`,
|
|
74
75
|
);
|
|
75
76
|
}
|
|
76
|
-
return
|
|
77
|
+
return {
|
|
78
|
+
object: obj,
|
|
79
|
+
name: obj.constructor.name,
|
|
80
|
+
params: checkedParams,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export enum LoadStatus {
|
|
85
|
+
Incompatible,
|
|
86
|
+
Changed,
|
|
87
|
+
Same,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface ChangeManager {
|
|
91
|
+
needInputReload(name: string): boolean;
|
|
92
|
+
needResourceReload(name: string): LoadStatus;
|
|
93
|
+
needExternalServiceReload(name: string, resource: string): boolean;
|
|
94
|
+
needMapperReload(name: string): boolean;
|
|
95
|
+
needReducerReload(name: string): boolean;
|
|
96
|
+
needLazyComputeReload(name: string): boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export class ServiceDefinition {
|
|
100
|
+
constructor(
|
|
101
|
+
private service: SkipService,
|
|
102
|
+
private readonly externals: Map<string, ExternalService> = new Map(),
|
|
103
|
+
) {}
|
|
104
|
+
|
|
105
|
+
buildResource(name: string, parameters: Json): Resource {
|
|
106
|
+
const builder = this.service.resources[name];
|
|
107
|
+
if (!builder) throw new Error(`Resource '${name}' not exist.`);
|
|
108
|
+
return new builder(parameters);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
inputs(): string[] {
|
|
112
|
+
return this.service.initialData
|
|
113
|
+
? Object.keys(this.service.initialData)
|
|
114
|
+
: [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
resources(): string[] {
|
|
118
|
+
return Object.keys(this.service.resources);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
initialData(name: string): Entry<Json, Json>[] {
|
|
122
|
+
if (!this.service.initialData) throw new Error(`No initial data defined.`);
|
|
123
|
+
const data = this.service.initialData[name];
|
|
124
|
+
if (!data) throw new Error(`Initial data '${name}' not exist.`);
|
|
125
|
+
return data;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
createGraph(
|
|
129
|
+
inputCollections: NamedCollections,
|
|
130
|
+
context: Context,
|
|
131
|
+
): NamedCollections {
|
|
132
|
+
return this.service.createGraph(inputCollections, context);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
subscribe(
|
|
136
|
+
external: string,
|
|
137
|
+
writer: CollectionWriter<Json, Json>,
|
|
138
|
+
instance: string,
|
|
139
|
+
resource: string,
|
|
140
|
+
params: Json,
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
if (!this.service.externalServices)
|
|
143
|
+
throw new Error(`No external services defined.`);
|
|
144
|
+
const supplier = this.service.externalServices[external];
|
|
145
|
+
if (!supplier)
|
|
146
|
+
throw new Error(`External services '${external}' not exist.`);
|
|
147
|
+
this.externals.set(`${external}/${instance}`, supplier);
|
|
148
|
+
// Ensure notification is made outside the current context update
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
supplier
|
|
152
|
+
.subscribe(instance, resource, params, {
|
|
153
|
+
update: writer.update.bind(writer),
|
|
154
|
+
error: (_) => {},
|
|
155
|
+
})
|
|
156
|
+
.then(resolve)
|
|
157
|
+
.catch(reject);
|
|
158
|
+
}, 0);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
unsubscribe(external: string, instance: string) {
|
|
163
|
+
if (!this.service.externalServices)
|
|
164
|
+
throw new Error(`No external services defined.`);
|
|
165
|
+
const supplier = this.externals.get(`${external}/${instance}`);
|
|
166
|
+
if (!supplier)
|
|
167
|
+
throw new Error(`External services '${external}/${instance}' not exist.`);
|
|
168
|
+
supplier.unsubscribe(instance);
|
|
169
|
+
this.externals.delete(`${external}/${instance}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async shutdown(): Promise<void> {
|
|
173
|
+
const promises: Promise<void>[] = [];
|
|
174
|
+
const uniqueServices = new Set(this.externals.values());
|
|
175
|
+
if (this.service.externalServices) {
|
|
176
|
+
for (const es of Object.values(this.service.externalServices)) {
|
|
177
|
+
uniqueServices.add(es);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const es of uniqueServices) {
|
|
181
|
+
promises.push(es.shutdown());
|
|
182
|
+
}
|
|
183
|
+
await Promise.all(promises);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
derive(service: SkipService): ServiceDefinition {
|
|
187
|
+
return new ServiceDefinition(service, new Map(this.externals));
|
|
188
|
+
}
|
|
77
189
|
}
|
|
78
190
|
|
|
79
191
|
class Handles {
|
|
@@ -122,35 +234,27 @@ export class Stack {
|
|
|
122
234
|
}
|
|
123
235
|
}
|
|
124
236
|
|
|
125
|
-
export class Refs {
|
|
126
|
-
constructor(
|
|
127
|
-
public readonly binding: FromBinding,
|
|
128
|
-
public readonly skjson: JsonConverter,
|
|
129
|
-
public readonly handles: Handles,
|
|
130
|
-
public readonly needGC: () => boolean,
|
|
131
|
-
public readonly runWithGC: <T>(fn: () => T) => T,
|
|
132
|
-
) {}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
237
|
class LazyCollectionImpl<K extends Json, V extends Json>
|
|
136
238
|
extends SkManaged
|
|
137
239
|
implements LazyCollection<K, V>
|
|
138
240
|
{
|
|
139
241
|
constructor(
|
|
140
|
-
|
|
141
|
-
private readonly refs:
|
|
242
|
+
readonly lazyCollection: string,
|
|
243
|
+
private readonly refs: ToBinding,
|
|
142
244
|
) {
|
|
143
245
|
super();
|
|
144
246
|
Object.freeze(this);
|
|
145
247
|
}
|
|
146
248
|
|
|
147
249
|
getArray(key: K): (V & DepSafe)[] {
|
|
148
|
-
return this.refs
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
this.refs.
|
|
152
|
-
|
|
153
|
-
|
|
250
|
+
return this.refs
|
|
251
|
+
.json()
|
|
252
|
+
.importJSON(
|
|
253
|
+
this.refs.binding.SkipRuntime_LazyCollection__getArray(
|
|
254
|
+
this.lazyCollection,
|
|
255
|
+
this.refs.json().exportJSON(key),
|
|
256
|
+
),
|
|
257
|
+
) as (V & DepSafe)[];
|
|
154
258
|
}
|
|
155
259
|
|
|
156
260
|
getUnique(key: K, _default?: { ifNone?: V; ifMany?: V }): V & DepSafe {
|
|
@@ -174,19 +278,21 @@ class EagerCollectionImpl<K extends Json, V extends Json>
|
|
|
174
278
|
{
|
|
175
279
|
constructor(
|
|
176
280
|
public readonly collection: string,
|
|
177
|
-
private readonly refs:
|
|
281
|
+
private readonly refs: ToBinding,
|
|
178
282
|
) {
|
|
179
283
|
super();
|
|
180
284
|
Object.freeze(this);
|
|
181
285
|
}
|
|
182
286
|
|
|
183
287
|
getArray(key: K): (V & DepSafe)[] {
|
|
184
|
-
return this.refs
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
this.refs.
|
|
188
|
-
|
|
189
|
-
|
|
288
|
+
return this.refs
|
|
289
|
+
.json()
|
|
290
|
+
.importJSON(
|
|
291
|
+
this.refs.binding.SkipRuntime_Collection__getArray(
|
|
292
|
+
this.collection,
|
|
293
|
+
this.refs.json().exportJSON(key),
|
|
294
|
+
),
|
|
295
|
+
) as (V & DepSafe)[];
|
|
190
296
|
}
|
|
191
297
|
|
|
192
298
|
getUnique(key: K, _default?: { ifNone?: V; ifMany?: V }): V & DepSafe {
|
|
@@ -216,7 +322,7 @@ class EagerCollectionImpl<K extends Json, V extends Json>
|
|
|
216
322
|
slices(...ranges: [K, K][]): EagerCollection<K, V> {
|
|
217
323
|
const skcollection = this.refs.binding.SkipRuntime_Collection__slice(
|
|
218
324
|
this.collection,
|
|
219
|
-
this.refs.
|
|
325
|
+
this.refs.json().exportJSON(ranges),
|
|
220
326
|
);
|
|
221
327
|
return this.derive<K, V>(skcollection);
|
|
222
328
|
}
|
|
@@ -263,18 +369,20 @@ class EagerCollectionImpl<K extends Json, V extends Json>
|
|
|
263
369
|
this.refs.handles.register(mapperObj),
|
|
264
370
|
);
|
|
265
371
|
|
|
266
|
-
if (
|
|
372
|
+
if (
|
|
373
|
+
sknative in reducerObj.object &&
|
|
374
|
+
typeof reducerObj.object[sknative] == "string"
|
|
375
|
+
) {
|
|
267
376
|
return this.derive<K2, Accum>(
|
|
268
377
|
this.refs.binding.SkipRuntime_Collection__nativeMapReduce(
|
|
269
378
|
this.collection,
|
|
270
379
|
skmapper,
|
|
271
|
-
reducerObj[sknative],
|
|
380
|
+
reducerObj.object[sknative],
|
|
272
381
|
),
|
|
273
382
|
);
|
|
274
383
|
} else {
|
|
275
384
|
const skreducer = this.refs.binding.SkipRuntime_createReducer(
|
|
276
385
|
this.refs.handles.register(reducerObj),
|
|
277
|
-
this.refs.skjson.exportJSON(reducerObj.initial),
|
|
278
386
|
);
|
|
279
387
|
return this.derive<K2, Accum>(
|
|
280
388
|
this.refs.binding.SkipRuntime_Collection__mapReduce(
|
|
@@ -292,17 +400,19 @@ class EagerCollectionImpl<K extends Json, V extends Json>
|
|
|
292
400
|
...params: Params
|
|
293
401
|
): EagerCollection<K, Accum> {
|
|
294
402
|
const reducerObj = instantiateUserObject("Reducer", reducer, params);
|
|
295
|
-
if (
|
|
403
|
+
if (
|
|
404
|
+
sknative in reducerObj.object &&
|
|
405
|
+
typeof reducerObj.object[sknative] == "string"
|
|
406
|
+
) {
|
|
296
407
|
return this.derive<K, Accum>(
|
|
297
408
|
this.refs.binding.SkipRuntime_Collection__nativeReduce(
|
|
298
409
|
this.collection,
|
|
299
|
-
reducerObj[sknative],
|
|
410
|
+
reducerObj.object[sknative],
|
|
300
411
|
),
|
|
301
412
|
);
|
|
302
413
|
} else {
|
|
303
414
|
const skreducer = this.refs.binding.SkipRuntime_createReducer(
|
|
304
415
|
this.refs.handles.register(reducerObj),
|
|
305
|
-
this.refs.skjson.exportJSON(reducerObj.initial),
|
|
306
416
|
);
|
|
307
417
|
return this.derive<K, Accum>(
|
|
308
418
|
this.refs.binding.SkipRuntime_Collection__reduce(
|
|
@@ -319,7 +429,7 @@ class EagerCollectionImpl<K extends Json, V extends Json>
|
|
|
319
429
|
);
|
|
320
430
|
const mapped = this.refs.binding.SkipRuntime_Collection__merge(
|
|
321
431
|
this.collection,
|
|
322
|
-
this.refs.
|
|
432
|
+
this.refs.json().exportJSON(otherNames),
|
|
323
433
|
);
|
|
324
434
|
return this.derive<K, V>(mapped);
|
|
325
435
|
}
|
|
@@ -340,72 +450,71 @@ class EagerCollectionImpl<K extends Json, V extends Json>
|
|
|
340
450
|
class CollectionWriter<K extends Json, V extends Json> {
|
|
341
451
|
constructor(
|
|
342
452
|
public readonly collection: string,
|
|
343
|
-
private readonly refs:
|
|
453
|
+
private readonly refs: ToBinding,
|
|
454
|
+
private forkName: Nullable<string>,
|
|
344
455
|
) {}
|
|
345
456
|
|
|
346
457
|
async update(values: Entry<K, V>[], isInit: boolean): Promise<void> {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
this.collection,
|
|
358
|
-
this.refs.skjson.exportJSON(values),
|
|
359
|
-
isInit,
|
|
360
|
-
this.refs.binding.SkipRuntime_createExecutor(exHdl),
|
|
361
|
-
);
|
|
362
|
-
});
|
|
363
|
-
if (errorHdl) reject(this.refs.handles.deleteHandle(errorHdl));
|
|
364
|
-
});
|
|
458
|
+
this.refs.setFork(this.getForkName());
|
|
459
|
+
const uuid = crypto.randomUUID();
|
|
460
|
+
const fork = this.fork(uuid);
|
|
461
|
+
try {
|
|
462
|
+
await fork.update_(values, isInit);
|
|
463
|
+
fork.merge();
|
|
464
|
+
} catch (ex: unknown) {
|
|
465
|
+
fork.abortFork();
|
|
466
|
+
throw ex;
|
|
467
|
+
}
|
|
365
468
|
}
|
|
366
469
|
|
|
367
|
-
|
|
470
|
+
private update_(values: Entry<K, V>[], isInit: boolean): Promise<void> {
|
|
471
|
+
this.refs.setFork(this.forkName);
|
|
368
472
|
if (!this.refs.needGC()) {
|
|
369
473
|
throw new SkipError("CollectionWriter.update cannot be performed.");
|
|
370
474
|
}
|
|
371
|
-
|
|
372
|
-
this.refs.binding.
|
|
475
|
+
return this.refs.runAsync(() =>
|
|
476
|
+
this.refs.binding.SkipRuntime_CollectionWriter__update(
|
|
373
477
|
this.collection,
|
|
374
|
-
this.refs.
|
|
478
|
+
this.refs.json().exportJSON(values),
|
|
479
|
+
isInit,
|
|
375
480
|
),
|
|
376
481
|
);
|
|
377
|
-
if (errorHdl) throw this.refs.handles.deleteHandle(errorHdl);
|
|
378
482
|
}
|
|
379
483
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const errorHdl = this.refs.runWithGC(() =>
|
|
385
|
-
this.refs.binding.SkipRuntime_CollectionWriter__initialized(
|
|
386
|
-
this.collection,
|
|
387
|
-
this.refs.skjson.exportJSON(error ? this.toJSONError(error) : null),
|
|
388
|
-
),
|
|
389
|
-
);
|
|
390
|
-
if (errorHdl) throw this.refs.handles.deleteHandle(errorHdl);
|
|
484
|
+
private fork(name: string): CollectionWriter<K, V> {
|
|
485
|
+
this.refs.setFork(this.forkName);
|
|
486
|
+
this.refs.fork(name);
|
|
487
|
+
return new CollectionWriter(this.collection, this.refs, name);
|
|
391
488
|
}
|
|
392
489
|
|
|
393
|
-
private
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if (typeof error == "string") return error;
|
|
398
|
-
return JSON.parse(
|
|
399
|
-
JSON.stringify(error, Object.getOwnPropertyNames(error)),
|
|
400
|
-
) as Json;
|
|
490
|
+
private merge(): void {
|
|
491
|
+
if (!this.forkName) throw new Error("Unable to merge fork on main.");
|
|
492
|
+
this.refs.setFork(this.forkName);
|
|
493
|
+
this.refs.merge();
|
|
401
494
|
}
|
|
402
|
-
}
|
|
403
495
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
496
|
+
private abortFork(): void {
|
|
497
|
+
if (!this.forkName) throw new Error("Unable to abord fork on main.");
|
|
498
|
+
this.refs.setFork(this.forkName);
|
|
499
|
+
this.refs.abortFork();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
private getForkName(): Nullable<string> {
|
|
503
|
+
const forkName = this.forkName;
|
|
504
|
+
if (!forkName) return null;
|
|
505
|
+
if (
|
|
506
|
+
!this.refs.runWithGC(() =>
|
|
507
|
+
this.refs.binding.SkipRuntime_Runtime__forkExists(forkName),
|
|
508
|
+
)
|
|
509
|
+
) {
|
|
510
|
+
this.forkName = null;
|
|
511
|
+
}
|
|
512
|
+
return this.forkName;
|
|
408
513
|
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
class ContextImpl implements Context {
|
|
517
|
+
constructor(private readonly refs: ToBinding) {}
|
|
409
518
|
|
|
410
519
|
createLazyCollection<
|
|
411
520
|
K extends Json,
|
|
@@ -433,15 +542,16 @@ class ContextImpl extends SkManaged implements Context {
|
|
|
433
542
|
this.refs.binding.SkipRuntime_Context__useExternalResource(
|
|
434
543
|
resource.service,
|
|
435
544
|
resource.identifier,
|
|
436
|
-
this.refs.
|
|
545
|
+
this.refs.json().exportJSON(resource.params ?? {}),
|
|
437
546
|
);
|
|
438
547
|
return new EagerCollectionImpl<K, V>(collection, this.refs);
|
|
439
548
|
}
|
|
440
549
|
|
|
441
550
|
jsonExtract(value: JsonObject, pattern: string): Json[] {
|
|
442
|
-
|
|
551
|
+
const skjson = this.refs.json();
|
|
552
|
+
return skjson.importJSON(
|
|
443
553
|
this.refs.binding.SkipRuntime_Context__jsonExtract(
|
|
444
|
-
|
|
554
|
+
skjson.exportJSON(value),
|
|
445
555
|
pattern,
|
|
446
556
|
),
|
|
447
557
|
) as Json[];
|
|
@@ -456,46 +566,39 @@ export class ServiceInstanceFactory {
|
|
|
456
566
|
}
|
|
457
567
|
}
|
|
458
568
|
|
|
459
|
-
type
|
|
460
|
-
payload: T;
|
|
461
|
-
errors: Json[];
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
export type SubscriptionID = Opaque<bigint, "subscription">;
|
|
569
|
+
export type SubscriptionID = Opaque<string, "subscription">;
|
|
465
570
|
|
|
466
571
|
/**
|
|
467
572
|
* A `ServiceInstance` is a running instance of a `SkipService`, providing access to its resources
|
|
468
573
|
* and operations to manage subscriptions and the service itself.
|
|
469
574
|
*/
|
|
470
575
|
export class ServiceInstance {
|
|
471
|
-
constructor(
|
|
576
|
+
constructor(
|
|
577
|
+
private readonly refs: ToBinding,
|
|
578
|
+
readonly forkName: Nullable<string>,
|
|
579
|
+
private definition: ServiceDefinition,
|
|
580
|
+
) {}
|
|
472
581
|
|
|
473
582
|
/**
|
|
474
583
|
* Instantiate a resource with some parameters and client session authentication token
|
|
475
584
|
* @param identifier - The resource instance identifier
|
|
476
585
|
* @param resource - A resource name, which must correspond to a key in this `SkipService`'s `resources` field
|
|
477
586
|
* @param params - Resource parameters, which will be passed to the resource constructor specified in this `SkipService`'s `resources` field
|
|
587
|
+
* @returns The resulting promise
|
|
478
588
|
*/
|
|
479
589
|
instantiateResource(
|
|
480
590
|
identifier: string,
|
|
481
591
|
resource: string,
|
|
482
592
|
params: Json,
|
|
483
593
|
): Promise<void> {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
resource,
|
|
493
|
-
this.refs.skjson.exportJSON(params),
|
|
494
|
-
this.refs.binding.SkipRuntime_createExecutor(exHdl),
|
|
495
|
-
);
|
|
496
|
-
});
|
|
497
|
-
if (errorHdl) reject(this.refs.handles.deleteHandle(errorHdl));
|
|
498
|
-
});
|
|
594
|
+
this.refs.setFork(this.forkName);
|
|
595
|
+
return this.refs.runAsync(() =>
|
|
596
|
+
this.refs.binding.SkipRuntime_Runtime__createResource(
|
|
597
|
+
identifier,
|
|
598
|
+
resource,
|
|
599
|
+
this.refs.json().exportJSON(params),
|
|
600
|
+
),
|
|
601
|
+
);
|
|
499
602
|
}
|
|
500
603
|
|
|
501
604
|
/**
|
|
@@ -511,21 +614,21 @@ export class ServiceInstance {
|
|
|
511
614
|
const uuid = crypto.randomUUID();
|
|
512
615
|
await this.instantiateResource(uuid, resource, params);
|
|
513
616
|
try {
|
|
617
|
+
this.refs.setFork(this.forkName);
|
|
514
618
|
const result = this.refs.runWithGC(() => {
|
|
515
|
-
return this.refs
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
this.refs.
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
619
|
+
return this.refs
|
|
620
|
+
.json()
|
|
621
|
+
.importJSON(
|
|
622
|
+
this.refs.binding.SkipRuntime_Runtime__getAll(
|
|
623
|
+
resource,
|
|
624
|
+
this.refs.json().exportJSON(params),
|
|
625
|
+
),
|
|
626
|
+
true,
|
|
627
|
+
);
|
|
522
628
|
});
|
|
523
629
|
if (typeof result == "number")
|
|
524
630
|
throw this.refs.handles.deleteHandle(result as Handle<Error>);
|
|
525
|
-
|
|
526
|
-
if (info.errors.length > 0)
|
|
527
|
-
throw new SkipError(JSON.stringify(info.errors));
|
|
528
|
-
return info.payload;
|
|
631
|
+
return result as Entry<K, V>[];
|
|
529
632
|
} finally {
|
|
530
633
|
this.closeResourceInstance(uuid);
|
|
531
634
|
}
|
|
@@ -546,22 +649,21 @@ export class ServiceInstance {
|
|
|
546
649
|
const uuid = crypto.randomUUID();
|
|
547
650
|
await this.instantiateResource(uuid, resource, params);
|
|
548
651
|
try {
|
|
652
|
+
this.refs.setFork(this.forkName);
|
|
653
|
+
const skjson = this.refs.json();
|
|
549
654
|
const result = this.refs.runWithGC(() => {
|
|
550
|
-
return
|
|
655
|
+
return skjson.importJSON(
|
|
551
656
|
this.refs.binding.SkipRuntime_Runtime__getForKey(
|
|
552
657
|
resource,
|
|
553
|
-
|
|
554
|
-
|
|
658
|
+
skjson.exportJSON(params),
|
|
659
|
+
skjson.exportJSON(key),
|
|
555
660
|
),
|
|
556
661
|
true,
|
|
557
662
|
);
|
|
558
663
|
});
|
|
559
664
|
if (typeof result == "number")
|
|
560
665
|
throw this.refs.handles.deleteHandle(result as Handle<Error>);
|
|
561
|
-
|
|
562
|
-
if (info.errors.length > 0)
|
|
563
|
-
throw new SkipError(JSON.stringify(info.errors));
|
|
564
|
-
return info.payload;
|
|
666
|
+
return result as V[];
|
|
565
667
|
} finally {
|
|
566
668
|
this.closeResourceInstance(uuid);
|
|
567
669
|
}
|
|
@@ -572,6 +674,7 @@ export class ServiceInstance {
|
|
|
572
674
|
* @param resourceInstanceId - The resource identifier
|
|
573
675
|
*/
|
|
574
676
|
closeResourceInstance(resourceInstanceId: string): void {
|
|
677
|
+
this.refs.setFork(this.forkName);
|
|
575
678
|
const errorHdl = this.refs.runWithGC(() => {
|
|
576
679
|
return this.refs.binding.SkipRuntime_Runtime__closeResource(
|
|
577
680
|
resourceInstanceId,
|
|
@@ -599,6 +702,7 @@ export class ServiceInstance {
|
|
|
599
702
|
},
|
|
600
703
|
watermark?: string,
|
|
601
704
|
): SubscriptionID {
|
|
705
|
+
this.refs.setFork(this.forkName);
|
|
602
706
|
const session = this.refs.runWithGC(() => {
|
|
603
707
|
const sknotifier = this.refs.binding.SkipRuntime_createNotifier(
|
|
604
708
|
this.refs.handles.register(notifier),
|
|
@@ -620,7 +724,7 @@ export class ServiceInstance {
|
|
|
620
724
|
} else if (session < 0n) {
|
|
621
725
|
throw this.refs.handles.deleteHandle(Number(-session) as Handle<Error>);
|
|
622
726
|
}
|
|
623
|
-
return
|
|
727
|
+
return resourceInstanceId as SubscriptionID;
|
|
624
728
|
}
|
|
625
729
|
|
|
626
730
|
/**
|
|
@@ -628,6 +732,7 @@ export class ServiceInstance {
|
|
|
628
732
|
* @param id - The subscription identifier returned by a call to `subscribe`
|
|
629
733
|
*/
|
|
630
734
|
unsubscribe(id: SubscriptionID): void {
|
|
735
|
+
this.refs.setFork(this.forkName);
|
|
631
736
|
const errorHdl = this.refs.runWithGC(() => {
|
|
632
737
|
return this.refs.binding.SkipRuntime_Runtime__unsubscribe(id);
|
|
633
738
|
});
|
|
@@ -641,24 +746,45 @@ export class ServiceInstance {
|
|
|
641
746
|
* @param collection - the name of the input collection to update
|
|
642
747
|
* @param entries - entries to update in the collection.
|
|
643
748
|
*/
|
|
644
|
-
update<K extends Json, V extends Json>(
|
|
749
|
+
async update<K extends Json, V extends Json>(
|
|
645
750
|
collection: string,
|
|
646
751
|
entries: Entry<K, V>[],
|
|
647
752
|
): Promise<void> {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
753
|
+
this.refs.setFork(this.forkName);
|
|
754
|
+
const uuid = crypto.randomUUID();
|
|
755
|
+
const fork = this.fork(uuid);
|
|
756
|
+
try {
|
|
757
|
+
await fork.update_(collection, entries);
|
|
758
|
+
fork.merge([]);
|
|
759
|
+
} catch (ex: unknown) {
|
|
760
|
+
fork.abortFork();
|
|
761
|
+
throw ex;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
private async update_<K extends Json, V extends Json>(
|
|
766
|
+
collection: string,
|
|
767
|
+
entries: Entry<K, V>[],
|
|
768
|
+
): Promise<void> {
|
|
769
|
+
this.refs.setFork(this.forkName);
|
|
770
|
+
const result = this.refs.runWithGC(() => {
|
|
771
|
+
const json = this.refs.json();
|
|
772
|
+
return json.importJSON(
|
|
773
|
+
this.refs.binding.SkipRuntime_Runtime__update(
|
|
655
774
|
collection,
|
|
656
|
-
this.refs.
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
if (errorHdl) reject(this.refs.handles.deleteHandle(errorHdl));
|
|
775
|
+
this.refs.json().exportJSON(entries),
|
|
776
|
+
),
|
|
777
|
+
true,
|
|
778
|
+
);
|
|
661
779
|
});
|
|
780
|
+
if (Array.isArray(result)) {
|
|
781
|
+
const handles = result as Handle<Promise<void>>[];
|
|
782
|
+
const promises = handles.map((h) => this.refs.handles.deleteHandle(h));
|
|
783
|
+
await Promise.all(promises);
|
|
784
|
+
} else {
|
|
785
|
+
const errorHdl = result as Handle<Error>;
|
|
786
|
+
throw this.refs.handles.deleteHandle(errorHdl);
|
|
787
|
+
}
|
|
662
788
|
}
|
|
663
789
|
|
|
664
790
|
/**
|
|
@@ -667,21 +793,98 @@ export class ServiceInstance {
|
|
|
667
793
|
* @returns The promise of externals services shutdowns
|
|
668
794
|
*/
|
|
669
795
|
close(): Promise<unknown> {
|
|
796
|
+
this.refs.setFork(this.forkName);
|
|
670
797
|
const result = this.refs.runWithGC(() => {
|
|
671
|
-
return this.refs.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
);
|
|
798
|
+
return this.refs.binding.SkipRuntime_closeService();
|
|
799
|
+
});
|
|
800
|
+
if (result >= 0) {
|
|
801
|
+
return this.refs.handles.deleteHandle(result as Handle<Promise<unknown>>);
|
|
802
|
+
} else {
|
|
803
|
+
const errorHdl = -(result as number) as Handle<Error>;
|
|
804
|
+
return Promise.reject(this.refs.handles.deleteHandle(errorHdl));
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
async reload(service: SkipService, changes: ChangeManager): Promise<void> {
|
|
809
|
+
if (this.forkName) {
|
|
810
|
+
throw new SkipError("Reload cannot be called in transaction.");
|
|
811
|
+
}
|
|
812
|
+
const definition = this.definition.derive(service);
|
|
813
|
+
this.refs.setFork(this.forkName);
|
|
814
|
+
const uuid = crypto.randomUUID();
|
|
815
|
+
const fork = this.fork(uuid);
|
|
816
|
+
let merged = false;
|
|
817
|
+
try {
|
|
818
|
+
const streamsToClose = await fork._reload(definition, changes);
|
|
819
|
+
fork.merge(streamsToClose);
|
|
820
|
+
merged = true;
|
|
821
|
+
this.closeResourceStreams(streamsToClose);
|
|
822
|
+
this.definition = definition;
|
|
823
|
+
} catch (ex: unknown) {
|
|
824
|
+
if (!merged) fork.abortFork();
|
|
825
|
+
throw ex;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
private async _reload(
|
|
830
|
+
definition: ServiceDefinition,
|
|
831
|
+
changes: ChangeManager,
|
|
832
|
+
): Promise<string[]> {
|
|
833
|
+
this.refs.setFork(this.forkName);
|
|
834
|
+
const result = this.refs.runWithGC(() => {
|
|
835
|
+
this.refs.changes = this.refs.handles.register(changes);
|
|
836
|
+
const skservicehHdl = this.refs.handles.register(definition);
|
|
837
|
+
const skservice =
|
|
838
|
+
this.refs.binding.SkipRuntime_createService(skservicehHdl);
|
|
839
|
+
const res = this.refs.binding.SkipRuntime_Runtime__reload(skservice);
|
|
840
|
+
this.refs.handles.deleteHandle(this.refs.changes);
|
|
841
|
+
this.refs.changes = null;
|
|
842
|
+
return this.refs.json().importJSON(res, true);
|
|
675
843
|
});
|
|
676
844
|
if (Array.isArray(result)) {
|
|
677
|
-
const handles = result as Handle<Promise<void>>[];
|
|
845
|
+
const [handles, res] = result as [Handle<Promise<void>>[], string[]];
|
|
678
846
|
const promises = handles.map((h) => this.refs.handles.deleteHandle(h));
|
|
679
|
-
|
|
847
|
+
await Promise.all(promises);
|
|
848
|
+
return res;
|
|
680
849
|
} else {
|
|
681
850
|
const errorHdl = result as Handle<Error>;
|
|
682
|
-
|
|
851
|
+
throw this.refs.handles.deleteHandle(errorHdl);
|
|
683
852
|
}
|
|
684
853
|
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Fork the service with current specified name.
|
|
857
|
+
* @param name - the name of the fork.
|
|
858
|
+
* @returns The forked ServiceInstance
|
|
859
|
+
*/
|
|
860
|
+
private fork(name: string): ServiceInstance {
|
|
861
|
+
if (this.forkName) throw new Error(`Unable to fork ${this.forkName}.`);
|
|
862
|
+
this.refs.setFork(this.forkName);
|
|
863
|
+
this.refs.fork(name);
|
|
864
|
+
return new ServiceInstance(this.refs, name, this.definition);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
private merge(ignore: string[]): void {
|
|
868
|
+
if (!this.forkName) throw new Error("Unable to merge fork on main.");
|
|
869
|
+
this.refs.setFork(this.forkName);
|
|
870
|
+
this.refs.merge(ignore);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
private abortFork(): void {
|
|
874
|
+
if (!this.forkName) throw new Error("Unable to abord fork on main.");
|
|
875
|
+
this.refs.setFork(this.forkName);
|
|
876
|
+
this.refs.abortFork();
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
private closeResourceStreams(streams: string[]): void {
|
|
880
|
+
this.refs.setFork(this.forkName);
|
|
881
|
+
const errorHdl = this.refs.runWithGC(() => {
|
|
882
|
+
return this.refs.binding.SkipRuntime_Runtime__closeResourceStreams(
|
|
883
|
+
this.refs.json().exportJSON(streams),
|
|
884
|
+
);
|
|
885
|
+
});
|
|
886
|
+
if (errorHdl) throw this.refs.handles.deleteHandle(errorHdl);
|
|
887
|
+
}
|
|
685
888
|
}
|
|
686
889
|
|
|
687
890
|
class ValuesImpl<T> implements Values<T> {
|
|
@@ -758,17 +961,21 @@ class ValuesImpl<T> implements Values<T> {
|
|
|
758
961
|
|
|
759
962
|
export class ToBinding {
|
|
760
963
|
private readonly stack: Stack;
|
|
761
|
-
private readonly handles: Handles;
|
|
762
964
|
private skjson?: JsonConverter;
|
|
965
|
+
private forkName: Nullable<string>;
|
|
966
|
+
readonly handles: Handles;
|
|
967
|
+
changes: Nullable<Handle<ChangeManager>>;
|
|
763
968
|
|
|
764
969
|
constructor(
|
|
765
|
-
|
|
766
|
-
|
|
970
|
+
public binding: FromBinding,
|
|
971
|
+
public runWithGC: <T>(fn: () => T) => T,
|
|
767
972
|
private getConverter: () => JsonConverter,
|
|
768
973
|
private getError: (skExc: Pointer<Internal.Exception>) => Error,
|
|
769
974
|
) {
|
|
770
975
|
this.stack = new Stack();
|
|
771
976
|
this.handles = new Handles();
|
|
977
|
+
this.forkName = null;
|
|
978
|
+
this.changes = null;
|
|
772
979
|
}
|
|
773
980
|
|
|
774
981
|
register<T>(v: T): Handle<T> {
|
|
@@ -795,17 +1002,29 @@ export class ToBinding {
|
|
|
795
1002
|
return this.stack.get();
|
|
796
1003
|
}
|
|
797
1004
|
|
|
1005
|
+
SkipRuntime_getFork(): Nullable<string> {
|
|
1006
|
+
return this.forkName;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
SkipRuntime_getChangeManager(): number {
|
|
1010
|
+
return this.changes ?? 0;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
setFork(name: Nullable<string>): void {
|
|
1014
|
+
this.forkName = name;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
798
1017
|
// Mapper
|
|
799
1018
|
|
|
800
1019
|
SkipRuntime_Mapper__mapEntry(
|
|
801
|
-
skmapper: Handle<JSONMapper
|
|
1020
|
+
skmapper: Handle<HandlerInfo<JSONMapper>>,
|
|
802
1021
|
key: Pointer<Internal.CJSON>,
|
|
803
1022
|
values: Pointer<Internal.NonEmptyIterator>,
|
|
804
1023
|
): Pointer<Internal.CJArray> {
|
|
805
1024
|
const skjson = this.getJsonConverter();
|
|
806
1025
|
const mapper = this.handles.get(skmapper);
|
|
807
|
-
const context = new ContextImpl(this
|
|
808
|
-
const result = mapper.mapEntry(
|
|
1026
|
+
const context = new ContextImpl(this);
|
|
1027
|
+
const result = mapper.object.mapEntry(
|
|
809
1028
|
skjson.importJSON(key) as Json,
|
|
810
1029
|
new ValuesImpl<Json>(skjson, this.binding, values),
|
|
811
1030
|
context,
|
|
@@ -813,27 +1032,65 @@ export class ToBinding {
|
|
|
813
1032
|
return skjson.exportJSON(Array.from(result));
|
|
814
1033
|
}
|
|
815
1034
|
|
|
816
|
-
|
|
1035
|
+
SkipRuntime_Mapper__getInfo(
|
|
1036
|
+
skmapper: Handle<HandlerInfo<JSONMapper>>,
|
|
1037
|
+
): Pointer<Internal.CJObject> {
|
|
1038
|
+
return this.getInfo(skmapper);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
SkipRuntime_Mapper__isEquals(
|
|
1042
|
+
mapper: Handle<HandlerInfo<JSONMapper>>,
|
|
1043
|
+
other: Handle<HandlerInfo<JSONMapper>>,
|
|
1044
|
+
): number {
|
|
1045
|
+
const object = this.handles.get(mapper);
|
|
1046
|
+
if (this.getChanges()?.needMapperReload(object.name)) {
|
|
1047
|
+
return 0;
|
|
1048
|
+
}
|
|
1049
|
+
return this.isEquals(mapper, other);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
SkipRuntime_deleteMapper(mapper: Handle<HandlerInfo<JSONMapper>>): void {
|
|
817
1053
|
this.handles.deleteHandle(mapper);
|
|
818
1054
|
}
|
|
819
1055
|
|
|
820
1056
|
// LazyCompute
|
|
821
1057
|
|
|
822
1058
|
SkipRuntime_LazyCompute__compute(
|
|
823
|
-
sklazyCompute: Handle<JSONLazyCompute
|
|
1059
|
+
sklazyCompute: Handle<HandlerInfo<JSONLazyCompute>>,
|
|
824
1060
|
self: string,
|
|
825
1061
|
skkey: Pointer<Internal.CJSON>,
|
|
826
1062
|
): Pointer<Internal.CJArray> {
|
|
827
1063
|
const skjson = this.getJsonConverter();
|
|
828
1064
|
const lazyCompute = this.handles.get(sklazyCompute);
|
|
829
|
-
const
|
|
830
|
-
|
|
1065
|
+
const context = new ContextImpl(this);
|
|
1066
|
+
const result = lazyCompute.object.compute(
|
|
1067
|
+
new LazyCollectionImpl<Json, Json>(self, this),
|
|
831
1068
|
skjson.importJSON(skkey) as Json,
|
|
1069
|
+
context,
|
|
832
1070
|
);
|
|
833
1071
|
return skjson.exportJSON(Array.from(result));
|
|
834
1072
|
}
|
|
835
1073
|
|
|
836
|
-
|
|
1074
|
+
SkipRuntime_LazyCompute__getInfo(
|
|
1075
|
+
lazyCompute: Handle<HandlerInfo<JSONLazyCompute>>,
|
|
1076
|
+
): Pointer<Internal.CJObject> {
|
|
1077
|
+
return this.getInfo(lazyCompute);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
SkipRuntime_LazyCompute__isEquals(
|
|
1081
|
+
lazyCompute: Handle<HandlerInfo<JSONLazyCompute>>,
|
|
1082
|
+
other: Handle<HandlerInfo<JSONLazyCompute>>,
|
|
1083
|
+
): number {
|
|
1084
|
+
const object = this.handles.get(lazyCompute);
|
|
1085
|
+
if (this.getChanges()?.needLazyComputeReload(object.name)) {
|
|
1086
|
+
return 0;
|
|
1087
|
+
}
|
|
1088
|
+
return this.isEquals(lazyCompute, other);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
SkipRuntime_deleteLazyCompute(
|
|
1092
|
+
lazyCompute: Handle<HandlerInfo<JSONLazyCompute>>,
|
|
1093
|
+
): void {
|
|
837
1094
|
this.handles.deleteHandle(lazyCompute);
|
|
838
1095
|
}
|
|
839
1096
|
|
|
@@ -849,11 +1106,10 @@ export class ToBinding {
|
|
|
849
1106
|
const keysIds = skjson.importJSON(skcollections) as {
|
|
850
1107
|
[key: string]: string;
|
|
851
1108
|
};
|
|
852
|
-
const refs = this.refs();
|
|
853
1109
|
for (const [key, name] of Object.entries(keysIds)) {
|
|
854
|
-
collections[key] = new EagerCollectionImpl<Json, Json>(name,
|
|
1110
|
+
collections[key] = new EagerCollectionImpl<Json, Json>(name, this);
|
|
855
1111
|
}
|
|
856
|
-
const collection = resource.instantiate(collections, new ContextImpl(
|
|
1112
|
+
const collection = resource.instantiate(collections, new ContextImpl(this));
|
|
857
1113
|
return EagerCollectionImpl.getName(collection);
|
|
858
1114
|
}
|
|
859
1115
|
|
|
@@ -861,28 +1117,10 @@ export class ToBinding {
|
|
|
861
1117
|
this.handles.deleteHandle(resource);
|
|
862
1118
|
}
|
|
863
1119
|
|
|
864
|
-
//
|
|
1120
|
+
// ServiceDefinition
|
|
865
1121
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
skparams: Pointer<Internal.CJObject>,
|
|
869
|
-
): Pointer<Internal.Resource> {
|
|
870
|
-
const skjson = this.getJsonConverter();
|
|
871
|
-
const builder = this.handles.get(skbuilder);
|
|
872
|
-
const resource = builder.build(skjson.importJSON(skparams) as Json);
|
|
873
|
-
return this.binding.SkipRuntime_createResource(
|
|
874
|
-
this.handles.register(resource),
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
SkipRuntime_deleteResourceBuilder(builder: Handle<ResourceBuilder>): void {
|
|
879
|
-
this.handles.deleteHandle(builder);
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// Service
|
|
883
|
-
|
|
884
|
-
SkipRuntime_Service__createGraph(
|
|
885
|
-
skservice: Handle<SkipService>,
|
|
1122
|
+
SkipRuntime_ServiceDefinition__createGraph(
|
|
1123
|
+
skservice: Handle<ServiceDefinition>,
|
|
886
1124
|
skcollections: Pointer<Internal.CJObject>,
|
|
887
1125
|
): Pointer<Internal.CJObject> {
|
|
888
1126
|
const skjson = this.getJsonConverter();
|
|
@@ -891,11 +1129,10 @@ export class ToBinding {
|
|
|
891
1129
|
const keysIds = skjson.importJSON(skcollections) as {
|
|
892
1130
|
[key: string]: string;
|
|
893
1131
|
};
|
|
894
|
-
const refs = this.refs();
|
|
895
1132
|
for (const [key, name] of Object.entries(keysIds)) {
|
|
896
|
-
collections[key] = new EagerCollectionImpl<Json, Json>(name,
|
|
1133
|
+
collections[key] = new EagerCollectionImpl<Json, Json>(name, this);
|
|
897
1134
|
}
|
|
898
|
-
const result = service.createGraph(collections, new ContextImpl(
|
|
1135
|
+
const result = service.createGraph(collections, new ContextImpl(this));
|
|
899
1136
|
const collectionsNames: { [name: string]: string } = {};
|
|
900
1137
|
for (const [name, collection] of Object.entries(result)) {
|
|
901
1138
|
collectionsNames[name] = EagerCollectionImpl.getName(collection);
|
|
@@ -903,10 +1140,111 @@ export class ToBinding {
|
|
|
903
1140
|
return skjson.exportJSON(collectionsNames);
|
|
904
1141
|
}
|
|
905
1142
|
|
|
906
|
-
|
|
1143
|
+
SkipRuntime_ServiceDefinition__inputs(
|
|
1144
|
+
skservice: Handle<ServiceDefinition>,
|
|
1145
|
+
): Pointer<Internal.CJArray<Internal.CJSON>> {
|
|
1146
|
+
const skjson = this.getJsonConverter();
|
|
1147
|
+
const service = this.handles.get(skservice);
|
|
1148
|
+
return skjson.exportJSON(service.inputs());
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
SkipRuntime_ServiceDefinition__resources(
|
|
1152
|
+
skservice: Handle<ServiceDefinition>,
|
|
1153
|
+
): Pointer<Internal.CJArray<Internal.CJSON>> {
|
|
1154
|
+
const skjson = this.getJsonConverter();
|
|
1155
|
+
const service = this.handles.get(skservice);
|
|
1156
|
+
return skjson.exportJSON(service.resources());
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
SkipRuntime_ServiceDefinition__initialData(
|
|
1160
|
+
skservice: Handle<ServiceDefinition>,
|
|
1161
|
+
name: string,
|
|
1162
|
+
): Pointer<Internal.CJArray<Internal.CJSON>> {
|
|
1163
|
+
const skjson = this.getJsonConverter();
|
|
1164
|
+
const service = this.handles.get(skservice);
|
|
1165
|
+
return skjson.exportJSON(service.initialData(name));
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
SkipRuntime_ServiceDefinition__buildResource(
|
|
1169
|
+
skservice: Handle<ServiceDefinition>,
|
|
1170
|
+
name: string,
|
|
1171
|
+
skparams: Pointer<Internal.CJObject>,
|
|
1172
|
+
): Pointer<Internal.Resource> {
|
|
1173
|
+
const skjson = this.getJsonConverter();
|
|
1174
|
+
const service = this.handles.get(skservice);
|
|
1175
|
+
const resource = service.buildResource(
|
|
1176
|
+
name,
|
|
1177
|
+
skjson.importJSON(skparams) as Json,
|
|
1178
|
+
);
|
|
1179
|
+
return this.binding.SkipRuntime_createResource(
|
|
1180
|
+
this.handles.register(resource),
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
SkipRuntime_ServiceDefinition__subscribe(
|
|
1185
|
+
skservice: Handle<ServiceDefinition>,
|
|
1186
|
+
external: string,
|
|
1187
|
+
writerId: string,
|
|
1188
|
+
instance: string,
|
|
1189
|
+
resource: string,
|
|
1190
|
+
skparams: Pointer<Internal.CJObject>,
|
|
1191
|
+
): Handle<Promise<void>> {
|
|
1192
|
+
const skjson = this.getJsonConverter();
|
|
1193
|
+
const service = this.handles.get(skservice);
|
|
1194
|
+
const writer = new CollectionWriter(writerId, this, this.forkName);
|
|
1195
|
+
const params = skjson.importJSON(skparams, true) as Json;
|
|
1196
|
+
return this.handles.register(
|
|
1197
|
+
service.subscribe(external, writer, instance, resource, params),
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
SkipRuntime_ServiceDefinition__unsubscribe(
|
|
1202
|
+
skservice: Handle<ServiceDefinition>,
|
|
1203
|
+
external: string,
|
|
1204
|
+
instance: string,
|
|
1205
|
+
): void {
|
|
1206
|
+
const service = this.handles.get(skservice);
|
|
1207
|
+
service.unsubscribe(external, instance);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
SkipRuntime_ServiceDefinition__shutdown(
|
|
1211
|
+
skservice: Handle<ServiceDefinition>,
|
|
1212
|
+
): Handle<Promise<unknown>> {
|
|
1213
|
+
const service = this.handles.get(skservice);
|
|
1214
|
+
return this.handles.register(service.shutdown());
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
SkipRuntime_deleteService(service: Handle<ServiceDefinition>): void {
|
|
907
1218
|
this.handles.deleteHandle(service);
|
|
908
1219
|
}
|
|
909
1220
|
|
|
1221
|
+
// Change manager
|
|
1222
|
+
|
|
1223
|
+
SkipRuntime_ChangeManager__needInputReload(
|
|
1224
|
+
skmanager: Handle<ChangeManager>,
|
|
1225
|
+
name: string,
|
|
1226
|
+
): number {
|
|
1227
|
+
const manager = this.handles.get(skmanager);
|
|
1228
|
+
return manager.needInputReload(name) ? 1 : 0;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
SkipRuntime_ChangeManager__needResourceReload(
|
|
1232
|
+
skmanager: Handle<ChangeManager>,
|
|
1233
|
+
name: string,
|
|
1234
|
+
): number {
|
|
1235
|
+
const manager = this.handles.get(skmanager);
|
|
1236
|
+
return manager.needResourceReload(name);
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
SkipRuntime_ChangeManager__needExternalServiceReload(
|
|
1240
|
+
skmanager: Handle<ChangeManager>,
|
|
1241
|
+
name: string,
|
|
1242
|
+
resource: string,
|
|
1243
|
+
): number {
|
|
1244
|
+
const manager = this.handles.get(skmanager);
|
|
1245
|
+
return manager.needExternalServiceReload(name, resource) ? 1 : 0;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
910
1248
|
// Notifier
|
|
911
1249
|
|
|
912
1250
|
SkipRuntime_Notifier__subscribed<K extends Json, V extends Json>(
|
|
@@ -948,15 +1286,23 @@ export class ToBinding {
|
|
|
948
1286
|
|
|
949
1287
|
// Reducer
|
|
950
1288
|
|
|
1289
|
+
SkipRuntime_Reducer__init(
|
|
1290
|
+
skreducer: Handle<HandlerInfo<Reducer<Json, Json>>>,
|
|
1291
|
+
): Pointer<Internal.CJSON> {
|
|
1292
|
+
const skjson = this.getJsonConverter();
|
|
1293
|
+
const reducer = this.handles.get(skreducer);
|
|
1294
|
+
return skjson.exportJSON(reducer.object.initial);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
951
1297
|
SkipRuntime_Reducer__add(
|
|
952
|
-
skreducer: Handle<Reducer<Json, Json
|
|
1298
|
+
skreducer: Handle<HandlerInfo<Reducer<Json, Json>>>,
|
|
953
1299
|
skacc: Nullable<Pointer<Internal.CJSON>>,
|
|
954
1300
|
skvalue: Pointer<Internal.CJSON>,
|
|
955
1301
|
): Pointer<Internal.CJSON> {
|
|
956
1302
|
const skjson = this.getJsonConverter();
|
|
957
1303
|
const reducer = this.handles.get(skreducer);
|
|
958
1304
|
return skjson.exportJSON(
|
|
959
|
-
reducer.add(
|
|
1305
|
+
reducer.object.add(
|
|
960
1306
|
skacc ? (skjson.importJSON(skacc) as Json) : null,
|
|
961
1307
|
skjson.importJSON(skvalue) as Json & DepSafe,
|
|
962
1308
|
),
|
|
@@ -964,165 +1310,216 @@ export class ToBinding {
|
|
|
964
1310
|
}
|
|
965
1311
|
|
|
966
1312
|
SkipRuntime_Reducer__remove(
|
|
967
|
-
skreducer: Handle<Reducer<Json, Json
|
|
1313
|
+
skreducer: Handle<HandlerInfo<Reducer<Json, Json>>>,
|
|
968
1314
|
skacc: Pointer<Internal.CJSON>,
|
|
969
1315
|
skvalue: Pointer<Internal.CJSON>,
|
|
970
1316
|
): Nullable<Pointer<Internal.CJSON>> {
|
|
971
1317
|
const skjson = this.getJsonConverter();
|
|
972
1318
|
const reducer = this.handles.get(skreducer);
|
|
973
1319
|
return skjson.exportJSON(
|
|
974
|
-
reducer.remove(
|
|
1320
|
+
reducer.object.remove(
|
|
975
1321
|
skjson.importJSON(skacc) as Json,
|
|
976
1322
|
skjson.importJSON(skvalue) as Json & DepSafe,
|
|
977
1323
|
),
|
|
978
1324
|
);
|
|
979
1325
|
}
|
|
980
1326
|
|
|
981
|
-
|
|
982
|
-
|
|
1327
|
+
SkipRuntime_Reducer__isEquals(
|
|
1328
|
+
reducer: Handle<HandlerInfo<Reducer<Json, Json>>>,
|
|
1329
|
+
other: Handle<HandlerInfo<Reducer<Json, Json>>>,
|
|
1330
|
+
): number {
|
|
1331
|
+
const object = this.handles.get(reducer);
|
|
1332
|
+
if (this.getChanges()?.needReducerReload(object.name)) {
|
|
1333
|
+
return 0;
|
|
1334
|
+
}
|
|
1335
|
+
return this.isEquals(reducer, other);
|
|
983
1336
|
}
|
|
984
1337
|
|
|
985
|
-
|
|
1338
|
+
SkipRuntime_Reducer__getInfo(
|
|
1339
|
+
reducer: Handle<HandlerInfo<Reducer<Json, Json>>>,
|
|
1340
|
+
): Pointer<Internal.CJObject> {
|
|
1341
|
+
return this.getInfo(reducer);
|
|
1342
|
+
}
|
|
986
1343
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
writerId: string,
|
|
990
|
-
instance: string,
|
|
991
|
-
resource: string,
|
|
992
|
-
skparams: Pointer<Internal.CJObject>,
|
|
1344
|
+
SkipRuntime_deleteReducer(
|
|
1345
|
+
reducer: Handle<HandlerInfo<Reducer<Json, Json>>>,
|
|
993
1346
|
): void {
|
|
994
|
-
|
|
995
|
-
const supplier = this.handles.get(sksupplier);
|
|
996
|
-
const writer = new CollectionWriter(writerId, this.refs());
|
|
997
|
-
const params = skjson.importJSON(skparams, true) as Json;
|
|
998
|
-
// Ensure notification is made outside the current context update
|
|
999
|
-
setTimeout(() => {
|
|
1000
|
-
supplier
|
|
1001
|
-
.subscribe(instance, resource, params, {
|
|
1002
|
-
update: writer.update.bind(writer),
|
|
1003
|
-
error: writer.error.bind(writer),
|
|
1004
|
-
})
|
|
1005
|
-
.then(() => writer.initialized())
|
|
1006
|
-
.catch((e: unknown) =>
|
|
1007
|
-
writer.initialized(
|
|
1008
|
-
e instanceof Error
|
|
1009
|
-
? e.message
|
|
1010
|
-
: JSON.stringify(e, Object.getOwnPropertyNames(e)),
|
|
1011
|
-
),
|
|
1012
|
-
);
|
|
1013
|
-
}, 0);
|
|
1347
|
+
this.handles.deleteHandle(reducer);
|
|
1014
1348
|
}
|
|
1015
1349
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1350
|
+
async initService(service: SkipService): Promise<ServiceInstance> {
|
|
1351
|
+
this.setFork(null);
|
|
1352
|
+
const uuid = crypto.randomUUID();
|
|
1353
|
+
this.fork(uuid);
|
|
1354
|
+
try {
|
|
1355
|
+
this.setFork(uuid);
|
|
1356
|
+
const definition = new ServiceDefinition(service);
|
|
1357
|
+
await this.initService_(definition);
|
|
1358
|
+
this.setFork(uuid);
|
|
1359
|
+
this.merge();
|
|
1360
|
+
return new ServiceInstance(this, null, definition);
|
|
1361
|
+
} catch (ex: unknown) {
|
|
1362
|
+
this.setFork(uuid);
|
|
1363
|
+
this.abortFork();
|
|
1364
|
+
throw ex;
|
|
1365
|
+
}
|
|
1022
1366
|
}
|
|
1023
1367
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1368
|
+
private initService_(definition: ServiceDefinition): Promise<void> {
|
|
1369
|
+
return this.runAsync(() => {
|
|
1370
|
+
const skservicehHdl = this.handles.register(definition);
|
|
1371
|
+
const skservice = this.binding.SkipRuntime_createService(skservicehHdl);
|
|
1372
|
+
return this.binding.SkipRuntime_initService(skservice);
|
|
1373
|
+
});
|
|
1029
1374
|
}
|
|
1030
1375
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1376
|
+
//
|
|
1377
|
+
public getJsonConverter() {
|
|
1378
|
+
if (this.skjson == undefined) {
|
|
1379
|
+
this.skjson = this.getConverter();
|
|
1380
|
+
}
|
|
1381
|
+
return this.skjson;
|
|
1033
1382
|
}
|
|
1034
1383
|
|
|
1035
|
-
|
|
1384
|
+
public needGC() {
|
|
1385
|
+
return this.SkipRuntime_getContext() == null;
|
|
1386
|
+
}
|
|
1036
1387
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
checker.resolve();
|
|
1388
|
+
public json() {
|
|
1389
|
+
return this.getJsonConverter();
|
|
1040
1390
|
}
|
|
1041
1391
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
checker.reject(this.handles.deleteHandle(error));
|
|
1392
|
+
fork(name: string): void {
|
|
1393
|
+
const errorHdl = this.runWithGC(() =>
|
|
1394
|
+
this.binding.SkipRuntime_Runtime__fork(name),
|
|
1395
|
+
);
|
|
1396
|
+
if (errorHdl) throw this.handles.deleteHandle(errorHdl);
|
|
1048
1397
|
}
|
|
1049
1398
|
|
|
1050
|
-
|
|
1051
|
-
this.
|
|
1399
|
+
merge(ignore: string[] = []): void {
|
|
1400
|
+
const errorHdl = this.runWithGC(() =>
|
|
1401
|
+
this.binding.SkipRuntime_Runtime__merge(this.json().exportJSON(ignore)),
|
|
1402
|
+
);
|
|
1403
|
+
if (errorHdl) throw this.handles.deleteHandle(errorHdl);
|
|
1052
1404
|
}
|
|
1053
1405
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
const skremote = refs.binding.SkipRuntime_createExternalService(
|
|
1065
|
-
refs.handles.register(remote),
|
|
1066
|
-
);
|
|
1067
|
-
refs.binding.SkipRuntime_ExternalServiceMap__add(
|
|
1068
|
-
skExternalServices,
|
|
1069
|
-
name,
|
|
1070
|
-
skremote,
|
|
1071
|
-
);
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
const skresources =
|
|
1075
|
-
refs.binding.SkipRuntime_ResourceBuilderMap__create();
|
|
1076
|
-
for (const [name, builder] of Object.entries(service.resources)) {
|
|
1077
|
-
const skbuilder = refs.binding.SkipRuntime_createResourceBuilder(
|
|
1078
|
-
refs.handles.register(new ResourceBuilder(builder)),
|
|
1079
|
-
);
|
|
1080
|
-
refs.binding.SkipRuntime_ResourceBuilderMap__add(
|
|
1081
|
-
skresources,
|
|
1082
|
-
name,
|
|
1083
|
-
skbuilder,
|
|
1084
|
-
);
|
|
1085
|
-
}
|
|
1086
|
-
const skservice = refs.binding.SkipRuntime_createService(
|
|
1087
|
-
refs.handles.register(service),
|
|
1088
|
-
refs.skjson.exportJSON(service.initialData ?? {}),
|
|
1089
|
-
skresources,
|
|
1090
|
-
skExternalServices,
|
|
1091
|
-
);
|
|
1092
|
-
const exHdl = refs.handles.register({
|
|
1093
|
-
resolve: () => {
|
|
1094
|
-
resolve(new ServiceInstance(refs));
|
|
1095
|
-
},
|
|
1096
|
-
reject: (ex: Error) => reject(ex),
|
|
1097
|
-
});
|
|
1098
|
-
return refs.binding.SkipRuntime_initService(
|
|
1099
|
-
skservice,
|
|
1100
|
-
refs.binding.SkipRuntime_createExecutor(exHdl),
|
|
1101
|
-
);
|
|
1102
|
-
});
|
|
1103
|
-
if (errorHdl) reject(refs.handles.deleteHandle(errorHdl));
|
|
1406
|
+
abortFork(): void {
|
|
1407
|
+
const errorHdl = this.runWithGC(() =>
|
|
1408
|
+
this.binding.SkipRuntime_Runtime__abortFork(),
|
|
1409
|
+
);
|
|
1410
|
+
if (errorHdl) throw this.handles.deleteHandle(errorHdl);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
async runAsync(fn: () => Pointer<Internal.CJSON>): Promise<void> {
|
|
1414
|
+
const result = this.runWithGC(() => {
|
|
1415
|
+
return this.json().importJSON(fn(), true);
|
|
1104
1416
|
});
|
|
1417
|
+
if (Array.isArray(result)) {
|
|
1418
|
+
const handles = result as Handle<Promise<void>>[];
|
|
1419
|
+
const promises = handles.map((h) => this.handles.deleteHandle(h));
|
|
1420
|
+
await Promise.all(promises);
|
|
1421
|
+
} else {
|
|
1422
|
+
const errorHdl = result as Handle<Error>;
|
|
1423
|
+
throw this.handles.deleteHandle(errorHdl);
|
|
1424
|
+
}
|
|
1105
1425
|
}
|
|
1106
1426
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
if (
|
|
1110
|
-
|
|
1427
|
+
private deepEquals(a: Nullable<Json>, b: Nullable<Json>) {
|
|
1428
|
+
// Same reference or both NaN
|
|
1429
|
+
if (a === b) return true;
|
|
1430
|
+
if (a !== a && b !== b) return true; // NaN check
|
|
1431
|
+
|
|
1432
|
+
// Different types or one is null
|
|
1433
|
+
if (typeof a !== typeof b || a === null || b === null) return false;
|
|
1434
|
+
|
|
1435
|
+
// Primitives already checked by ===
|
|
1436
|
+
if (typeof a !== "object" || typeof b !== "object") return false;
|
|
1437
|
+
|
|
1438
|
+
// Arrays
|
|
1439
|
+
if (Array.isArray(a)) {
|
|
1440
|
+
if (!Array.isArray(b) || a.length !== b.length) return false;
|
|
1441
|
+
for (let i = 0; i < a.length; i++) {
|
|
1442
|
+
if (!this.deepEquals(a[i]!, b[i]!)) return false;
|
|
1443
|
+
}
|
|
1444
|
+
return true;
|
|
1111
1445
|
}
|
|
1112
|
-
|
|
1446
|
+
|
|
1447
|
+
// Different array status
|
|
1448
|
+
if (Array.isArray(b)) return false;
|
|
1449
|
+
|
|
1450
|
+
// Objects
|
|
1451
|
+
const keysA = Object.keys(a);
|
|
1452
|
+
const keysB = Object.keys(b);
|
|
1453
|
+
|
|
1454
|
+
if (keysA.length !== keysB.length) return false;
|
|
1455
|
+
|
|
1456
|
+
for (const key of keysA) {
|
|
1457
|
+
if (
|
|
1458
|
+
!Object.prototype.hasOwnProperty.call(b, key) ||
|
|
1459
|
+
!this.deepEquals(a[key]!, b[key]!)
|
|
1460
|
+
)
|
|
1461
|
+
return false;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
return true;
|
|
1113
1465
|
}
|
|
1114
1466
|
|
|
1115
|
-
private
|
|
1116
|
-
|
|
1467
|
+
private getInfo<T>(
|
|
1468
|
+
skmapper: Handle<HandlerInfo<T>>,
|
|
1469
|
+
): Pointer<Internal.CJObject> {
|
|
1470
|
+
const skjson = this.getJsonConverter();
|
|
1471
|
+
const object = this.handles.get(skmapper);
|
|
1472
|
+
const name = object.name;
|
|
1473
|
+
const parameters = object.params.map((v) => {
|
|
1474
|
+
if (v instanceof EagerCollectionImpl) {
|
|
1475
|
+
return {
|
|
1476
|
+
type: "collection",
|
|
1477
|
+
value: v.collection,
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
if (v instanceof LazyCollectionImpl) {
|
|
1481
|
+
return {
|
|
1482
|
+
type: "collection",
|
|
1483
|
+
value: v.lazyCollection,
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
return { type: "data", value: v as Json };
|
|
1487
|
+
});
|
|
1488
|
+
return skjson.exportJSON({ name, parameters });
|
|
1117
1489
|
}
|
|
1118
1490
|
|
|
1119
|
-
private
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1491
|
+
private isEquals<T extends JSONOperator>(
|
|
1492
|
+
mapper: Handle<HandlerInfo<T>>,
|
|
1493
|
+
other: Handle<HandlerInfo<T>>,
|
|
1494
|
+
): number {
|
|
1495
|
+
const object = this.handles.get(mapper);
|
|
1496
|
+
const oobject = this.handles.get(other);
|
|
1497
|
+
if (object.object.constructor != oobject.object.constructor) {
|
|
1498
|
+
return 0;
|
|
1499
|
+
}
|
|
1500
|
+
if (object.params.length != oobject.params.length) return 0;
|
|
1501
|
+
for (const [i, param] of object.params.entries()) {
|
|
1502
|
+
const oparam = oobject.params[i];
|
|
1503
|
+
if (param instanceof EagerCollectionImpl) {
|
|
1504
|
+
if (oparam instanceof EagerCollectionImpl) {
|
|
1505
|
+
if (param.collection != oparam.collection) return 0;
|
|
1506
|
+
} else {
|
|
1507
|
+
return 0;
|
|
1508
|
+
}
|
|
1509
|
+
} else if (param instanceof LazyCollectionImpl) {
|
|
1510
|
+
if (oparam instanceof LazyCollectionImpl) {
|
|
1511
|
+
if (param.lazyCollection != oparam.lazyCollection) return 0;
|
|
1512
|
+
} else {
|
|
1513
|
+
return 0;
|
|
1514
|
+
}
|
|
1515
|
+
} else if (!this.deepEquals(param as Json, oparam as Json)) {
|
|
1516
|
+
return 0;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
return 1;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
private getChanges(): Nullable<ChangeManager> {
|
|
1523
|
+
return this.changes ? this.handles.get(this.changes) : null;
|
|
1127
1524
|
}
|
|
1128
1525
|
}
|