@skipruntime/core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/binding.d.ts +70 -0
- package/dist/binding.d.ts.map +1 -0
- package/dist/binding.js +11 -0
- package/dist/binding.js.map +1 -0
- package/dist/errors.d.ts +3 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +3 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +206 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +707 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +38 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +2 -0
- package/dist/internal.js.map +1 -0
- package/dist/remote.d.ts +22 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +71 -0
- package/dist/remote.js.map +1 -0
- package/dist/utils.d.ts +22 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +40 -0
- package/dist/utils.js.map +1 -0
- package/eslint.config.js +3 -0
- package/package.json +18 -0
- package/src/binding.ts +258 -0
- package/src/errors.ts +1 -0
- package/src/index.ts +1134 -0
- package/src/internal.ts +54 -0
- package/src/remote.ts +94 -0
- package/src/utils.ts +48 -0
- package/tsconfig.json +3 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,1134 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Pointer,
|
|
3
|
+
Nullable,
|
|
4
|
+
Json,
|
|
5
|
+
JsonConverter,
|
|
6
|
+
JsonObject,
|
|
7
|
+
} from "@skiplang/json";
|
|
8
|
+
import { isObjectProxy, sk_freeze, isSkFrozen } from "@skiplang/json";
|
|
9
|
+
import type * as Internal from "./internal.js";
|
|
10
|
+
import {
|
|
11
|
+
NonUniqueValueException,
|
|
12
|
+
type CollectionUpdate,
|
|
13
|
+
type Context,
|
|
14
|
+
type EagerCollection,
|
|
15
|
+
type Entry,
|
|
16
|
+
type ExternalService,
|
|
17
|
+
type LazyCollection,
|
|
18
|
+
type LazyCompute,
|
|
19
|
+
type Mapper,
|
|
20
|
+
type NamedCollections,
|
|
21
|
+
type NonEmptyIterator,
|
|
22
|
+
type Param,
|
|
23
|
+
type Reducer,
|
|
24
|
+
type Resource,
|
|
25
|
+
type SkipService,
|
|
26
|
+
type SubscriptionID,
|
|
27
|
+
type Watermark,
|
|
28
|
+
} from "@skipruntime/api";
|
|
29
|
+
|
|
30
|
+
import { Frozen } from "@skipruntime/api/internals.js";
|
|
31
|
+
import { UnknownCollectionError } from "./errors.js";
|
|
32
|
+
import {
|
|
33
|
+
ResourceBuilder,
|
|
34
|
+
type Notifier,
|
|
35
|
+
type Checker,
|
|
36
|
+
type Handle,
|
|
37
|
+
type FromBinding,
|
|
38
|
+
} from "./binding.js";
|
|
39
|
+
|
|
40
|
+
export { UnknownCollectionError, sk_freeze, isSkFrozen };
|
|
41
|
+
export { SkipExternalService } from "./remote.js";
|
|
42
|
+
export { Sum, Min, Max, CountMapper } from "./utils.js";
|
|
43
|
+
|
|
44
|
+
export type JSONMapper = Mapper<Json, Json, Json, Json>;
|
|
45
|
+
export type JSONLazyCompute = LazyCompute<Json, Json>;
|
|
46
|
+
|
|
47
|
+
export type Entrypoint = {
|
|
48
|
+
host: string;
|
|
49
|
+
streaming_port: number;
|
|
50
|
+
control_port: number;
|
|
51
|
+
secured?: boolean;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
abstract class SkFrozen extends Frozen {
|
|
55
|
+
protected freeze() {
|
|
56
|
+
sk_freeze(this);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function checkOrCloneParam<T>(value: T): T {
|
|
61
|
+
if (
|
|
62
|
+
typeof value == "string" ||
|
|
63
|
+
typeof value == "number" ||
|
|
64
|
+
typeof value == "boolean"
|
|
65
|
+
)
|
|
66
|
+
return value;
|
|
67
|
+
if (typeof value == "object") {
|
|
68
|
+
if (value === null) return value;
|
|
69
|
+
if (isObjectProxy(value)) return value.clone() as T;
|
|
70
|
+
if (isSkFrozen(value)) return value;
|
|
71
|
+
throw new Error("Invalid object: must be deep-frozen.");
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`'${typeof value}' cannot be deep-frozen.`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* _Deep-freeze_ an object, returning the same object that was passed in.
|
|
78
|
+
*
|
|
79
|
+
* This function is similar to
|
|
80
|
+
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze | `Object.freeze()`}
|
|
81
|
+
* but freezes the object and deep-freezes all its properties,
|
|
82
|
+
* recursively. The object is then not only _immutable_ but also
|
|
83
|
+
* _constant_. Note that as a result all objects reachable from the
|
|
84
|
+
* parameter will be frozen and no longer mutable or extensible, even from
|
|
85
|
+
* other references.
|
|
86
|
+
*
|
|
87
|
+
* The argument object and all its properties, recursively, must not already
|
|
88
|
+
* be frozen by `Object.freeze` (or else `deepFreeze` cannot mark them
|
|
89
|
+
* deep-frozen). Undefined, function (and hence class) values cannot be
|
|
90
|
+
* deep-frozen.
|
|
91
|
+
*
|
|
92
|
+
* The primary use for this function is to satisfy the requirement that all
|
|
93
|
+
* parameters to Skip `Mapper` constructors must be deep-frozen: objects
|
|
94
|
+
* that have not been constructed by Skip can be passed to `deepFreeze()`
|
|
95
|
+
* before passing them to a `Mapper` constructor.
|
|
96
|
+
*
|
|
97
|
+
* @param value - The object to deep-freeze.
|
|
98
|
+
* @returns The same object that was passed in.
|
|
99
|
+
*/
|
|
100
|
+
export function deepFreeze<T>(value: T): T & Param {
|
|
101
|
+
if (
|
|
102
|
+
typeof value == "bigint" ||
|
|
103
|
+
typeof value == "boolean" ||
|
|
104
|
+
typeof value == "number" ||
|
|
105
|
+
typeof value == "string" ||
|
|
106
|
+
typeof value == "symbol"
|
|
107
|
+
) {
|
|
108
|
+
return value;
|
|
109
|
+
} else if (typeof value == "object") {
|
|
110
|
+
if (value === null) {
|
|
111
|
+
return value;
|
|
112
|
+
} else if (isSkFrozen(value)) {
|
|
113
|
+
return value;
|
|
114
|
+
} else if (Object.isFrozen(value)) {
|
|
115
|
+
throw new Error(`Cannot deep-freeze an Object.frozen value.`);
|
|
116
|
+
} else if (Array.isArray(value)) {
|
|
117
|
+
for (const elt of value) {
|
|
118
|
+
deepFreeze(elt);
|
|
119
|
+
}
|
|
120
|
+
return Object.freeze(sk_freeze(value));
|
|
121
|
+
} else {
|
|
122
|
+
for (const val of Object.values(value)) {
|
|
123
|
+
deepFreeze(val);
|
|
124
|
+
}
|
|
125
|
+
return Object.freeze(sk_freeze(value));
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
// typeof value == "function" || typeof value == "undefined"
|
|
129
|
+
throw new Error(`'${typeof value}' values cannot be deep-frozen.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
class Handles {
|
|
134
|
+
private nextID: number = 1;
|
|
135
|
+
private readonly objects: any[] = [];
|
|
136
|
+
private readonly freeIDs: number[] = [];
|
|
137
|
+
|
|
138
|
+
register<T>(v: T): Handle<T> {
|
|
139
|
+
const freeID = this.freeIDs.pop();
|
|
140
|
+
const id = freeID ?? this.nextID++;
|
|
141
|
+
this.objects[id] = v;
|
|
142
|
+
return id as Handle<T>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
get<T>(id: Handle<T>): T {
|
|
146
|
+
return this.objects[id] as T;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
apply<R, P extends any[]>(id: Handle<(..._: P) => R>, parameters: P): R {
|
|
150
|
+
const fn = this.get(id);
|
|
151
|
+
return fn.apply(null, parameters);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
deleteHandle<T>(id: Handle<T>): T {
|
|
155
|
+
const current = this.get(id);
|
|
156
|
+
this.objects[id] = null;
|
|
157
|
+
this.freeIDs.push(id);
|
|
158
|
+
return current;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export class Stack {
|
|
163
|
+
private readonly stack: Pointer<Internal.Context>[] = [];
|
|
164
|
+
|
|
165
|
+
push(pointer: Pointer<Internal.Context>) {
|
|
166
|
+
this.stack.push(pointer);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
get(): Nullable<Pointer<Internal.Context>> {
|
|
170
|
+
if (this.stack.length == 0) return null;
|
|
171
|
+
return this.stack[this.stack.length - 1]!;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
pop(): void {
|
|
175
|
+
this.stack.pop();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export class Refs {
|
|
180
|
+
constructor(
|
|
181
|
+
public readonly binding: FromBinding,
|
|
182
|
+
public readonly skjson: JsonConverter,
|
|
183
|
+
public readonly handles: Handles,
|
|
184
|
+
public readonly needGC: () => boolean,
|
|
185
|
+
public readonly runWithGC: <T>(fn: () => T) => T,
|
|
186
|
+
) {}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
class LazyCollectionImpl<K extends Json, V extends Json>
|
|
190
|
+
extends SkFrozen
|
|
191
|
+
implements LazyCollection<K, V>
|
|
192
|
+
{
|
|
193
|
+
constructor(
|
|
194
|
+
private readonly lazyCollection: string,
|
|
195
|
+
private readonly refs: Refs,
|
|
196
|
+
) {
|
|
197
|
+
super();
|
|
198
|
+
Object.freeze(this);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
getArray(key: K): (V & Param)[] {
|
|
202
|
+
return this.refs.skjson.importJSON(
|
|
203
|
+
this.refs.binding.SkipRuntime_LazyCollection__getArray(
|
|
204
|
+
this.lazyCollection,
|
|
205
|
+
this.refs.skjson.exportJSON(key),
|
|
206
|
+
),
|
|
207
|
+
) as (V & Param)[];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getUnique(key: K): V & Param {
|
|
211
|
+
const v = this.refs.skjson.importOptJSON(
|
|
212
|
+
this.refs.binding.SkipRuntime_LazyCollection__getUnique(
|
|
213
|
+
this.lazyCollection,
|
|
214
|
+
this.refs.skjson.exportJSON(key),
|
|
215
|
+
),
|
|
216
|
+
) as Nullable<V & Param>;
|
|
217
|
+
if (v == null) throw new NonUniqueValueException();
|
|
218
|
+
return v;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
class EagerCollectionImpl<K extends Json, V extends Json>
|
|
223
|
+
extends SkFrozen
|
|
224
|
+
implements EagerCollection<K, V>
|
|
225
|
+
{
|
|
226
|
+
constructor(
|
|
227
|
+
public readonly collection: string,
|
|
228
|
+
private readonly refs: Refs,
|
|
229
|
+
) {
|
|
230
|
+
super();
|
|
231
|
+
Object.freeze(this);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
getArray(key: K): (V & Param)[] {
|
|
235
|
+
return this.refs.skjson.importJSON(
|
|
236
|
+
this.refs.binding.SkipRuntime_Collection__getArray(
|
|
237
|
+
this.collection,
|
|
238
|
+
this.refs.skjson.exportJSON(key),
|
|
239
|
+
),
|
|
240
|
+
) as (V & Param)[];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
getUnique(key: K): V & Param {
|
|
244
|
+
const v = this.refs.skjson.importOptJSON(
|
|
245
|
+
this.refs.binding.SkipRuntime_Collection__getUnique(
|
|
246
|
+
this.collection,
|
|
247
|
+
this.refs.skjson.exportJSON(key),
|
|
248
|
+
),
|
|
249
|
+
) as Nullable<V & Param>;
|
|
250
|
+
if (v == null) throw new NonUniqueValueException();
|
|
251
|
+
return v;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
size = () => {
|
|
255
|
+
return Number(
|
|
256
|
+
this.refs.binding.SkipRuntime_Collection__size(this.collection),
|
|
257
|
+
);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
slice(start: K, end: K): EagerCollection<K, V> {
|
|
261
|
+
return this.slices([start, end]);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
slices(...ranges: [K, K][]): EagerCollection<K, V> {
|
|
265
|
+
const skcollection = this.refs.binding.SkipRuntime_Collection__slice(
|
|
266
|
+
this.collection,
|
|
267
|
+
this.refs.skjson.exportJSON(ranges),
|
|
268
|
+
);
|
|
269
|
+
return this.derive<K, V>(skcollection);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
take(limit: number): EagerCollection<K, V> {
|
|
273
|
+
const skcollection = this.refs.binding.SkipRuntime_Collection__take(
|
|
274
|
+
this.collection,
|
|
275
|
+
BigInt(limit),
|
|
276
|
+
);
|
|
277
|
+
return this.derive<K, V>(skcollection);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
map<K2 extends Json, V2 extends Json, Params extends Param[]>(
|
|
281
|
+
mapper: new (...params: Params) => Mapper<K, V, K2, V2>,
|
|
282
|
+
...params: Params
|
|
283
|
+
): EagerCollection<K2, V2> {
|
|
284
|
+
const mapperParams = params.map(checkOrCloneParam) as Params;
|
|
285
|
+
const mapperObj = new mapper(...mapperParams);
|
|
286
|
+
Object.freeze(mapperObj);
|
|
287
|
+
if (!mapperObj.constructor.name) {
|
|
288
|
+
throw new Error("Mapper classes must be defined at top-level.");
|
|
289
|
+
}
|
|
290
|
+
const skmapper = this.refs.binding.SkipRuntime_createMapper(
|
|
291
|
+
this.refs.handles.register(mapperObj),
|
|
292
|
+
);
|
|
293
|
+
const mapped = this.refs.binding.SkipRuntime_Collection__map(
|
|
294
|
+
this.collection,
|
|
295
|
+
skmapper,
|
|
296
|
+
);
|
|
297
|
+
return this.derive<K2, V2>(mapped);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
mapReduce<K2 extends Json, V2 extends Json, MapperParams extends Param[]>(
|
|
301
|
+
mapper: new (...params: MapperParams) => Mapper<K, V, K2, V2>,
|
|
302
|
+
...mapperParams: MapperParams
|
|
303
|
+
) {
|
|
304
|
+
return <Accum extends Json, ReducerParams extends Param[]>(
|
|
305
|
+
reducer: new (...params: ReducerParams) => Reducer<V2, Accum>,
|
|
306
|
+
...reducerParams: ReducerParams
|
|
307
|
+
) => {
|
|
308
|
+
const mParams = mapperParams.map(checkOrCloneParam) as MapperParams;
|
|
309
|
+
const rParams = reducerParams.map(checkOrCloneParam) as ReducerParams;
|
|
310
|
+
|
|
311
|
+
const mapperObj = new mapper(...mParams);
|
|
312
|
+
const reducerObj = new reducer(...rParams);
|
|
313
|
+
|
|
314
|
+
Object.freeze(mapperObj);
|
|
315
|
+
Object.freeze(reducerObj);
|
|
316
|
+
|
|
317
|
+
if (!mapperObj.constructor.name) {
|
|
318
|
+
throw new Error("Mapper classes must be defined at top-level.");
|
|
319
|
+
}
|
|
320
|
+
if (!reducerObj.constructor.name) {
|
|
321
|
+
throw new Error("Reducer classes must be defined at top-level.");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const skmapper = this.refs.binding.SkipRuntime_createMapper(
|
|
325
|
+
this.refs.handles.register(mapperObj),
|
|
326
|
+
);
|
|
327
|
+
const skreducer = this.refs.binding.SkipRuntime_createReducer(
|
|
328
|
+
this.refs.handles.register(reducerObj),
|
|
329
|
+
this.refs.skjson.exportJSON(reducerObj.default),
|
|
330
|
+
);
|
|
331
|
+
const mapped = this.refs.binding.SkipRuntime_Collection__mapReduce(
|
|
332
|
+
this.collection,
|
|
333
|
+
skmapper,
|
|
334
|
+
skreducer,
|
|
335
|
+
);
|
|
336
|
+
return this.derive<K2, Accum>(mapped);
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
reduce<Accum extends Json, Params extends Param[]>(
|
|
341
|
+
reducer: new (...params: Params) => Reducer<V, Accum>,
|
|
342
|
+
...params: Params
|
|
343
|
+
): EagerCollection<K, Accum> {
|
|
344
|
+
const reducerParams = params.map(checkOrCloneParam) as Params;
|
|
345
|
+
const reducerObj = new reducer(...reducerParams);
|
|
346
|
+
Object.freeze(reducerObj);
|
|
347
|
+
if (!reducerObj.constructor.name) {
|
|
348
|
+
throw new Error("Reducer classes must be defined at top-level.");
|
|
349
|
+
}
|
|
350
|
+
const skreducer = this.refs.binding.SkipRuntime_createReducer(
|
|
351
|
+
this.refs.handles.register(reducerObj),
|
|
352
|
+
this.refs.skjson.exportJSON(reducerObj.default),
|
|
353
|
+
);
|
|
354
|
+
return this.derive<K, Accum>(
|
|
355
|
+
this.refs.binding.SkipRuntime_Collection__reduce(
|
|
356
|
+
this.collection,
|
|
357
|
+
skreducer,
|
|
358
|
+
),
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
merge(...others: EagerCollection<K, V>[]): EagerCollection<K, V> {
|
|
363
|
+
const otherNames = others.map(
|
|
364
|
+
(other) => (other as EagerCollectionImpl<K, V>).collection,
|
|
365
|
+
);
|
|
366
|
+
const mapped = this.refs.binding.SkipRuntime_Collection__merge(
|
|
367
|
+
this.collection,
|
|
368
|
+
this.refs.skjson.exportJSON(otherNames),
|
|
369
|
+
);
|
|
370
|
+
return this.derive<K, V>(mapped);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private derive<K2 extends Json, V2 extends Json>(
|
|
374
|
+
collection: string,
|
|
375
|
+
): EagerCollection<K2, V2> {
|
|
376
|
+
return new EagerCollectionImpl<K2, V2>(collection, this.refs);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
class CollectionWriter<K extends Json, V extends Json> {
|
|
381
|
+
constructor(
|
|
382
|
+
public readonly collection: string,
|
|
383
|
+
private readonly refs: Refs,
|
|
384
|
+
) {}
|
|
385
|
+
|
|
386
|
+
update(values: Entry<K, V>[], isInit: boolean): void {
|
|
387
|
+
const update_ = () => {
|
|
388
|
+
return this.refs.binding.SkipRuntime_CollectionWriter__update(
|
|
389
|
+
this.collection,
|
|
390
|
+
this.refs.skjson.exportJSON(values),
|
|
391
|
+
isInit,
|
|
392
|
+
);
|
|
393
|
+
};
|
|
394
|
+
if (this.refs.needGC()) {
|
|
395
|
+
this.refs.runWithGC(update_);
|
|
396
|
+
} else {
|
|
397
|
+
update_();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
loading(): void {
|
|
402
|
+
const loading_ = () => {
|
|
403
|
+
return this.refs.binding.SkipRuntime_CollectionWriter__loading(
|
|
404
|
+
this.collection,
|
|
405
|
+
);
|
|
406
|
+
};
|
|
407
|
+
if (this.refs.needGC()) this.refs.runWithGC(loading_);
|
|
408
|
+
else loading_();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
error(error: Json): void {
|
|
412
|
+
const error_ = () => {
|
|
413
|
+
return this.refs.binding.SkipRuntime_CollectionWriter__error(
|
|
414
|
+
this.collection,
|
|
415
|
+
this.refs.skjson.exportJSON(error),
|
|
416
|
+
);
|
|
417
|
+
};
|
|
418
|
+
if (this.refs.needGC()) this.refs.runWithGC(error_);
|
|
419
|
+
else error_();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
class ContextImpl extends SkFrozen implements Context {
|
|
424
|
+
constructor(private readonly refs: Refs) {
|
|
425
|
+
super();
|
|
426
|
+
Object.freeze(this);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
createLazyCollection<K extends Json, V extends Json, Params extends Param[]>(
|
|
430
|
+
compute: new (...params: Params) => LazyCompute<K, V>,
|
|
431
|
+
...params: Params
|
|
432
|
+
): LazyCollection<K, V> {
|
|
433
|
+
const mapperParams = params.map(checkOrCloneParam) as Params;
|
|
434
|
+
const computeObj = new compute(...mapperParams);
|
|
435
|
+
Object.freeze(computeObj);
|
|
436
|
+
if (!computeObj.constructor.name) {
|
|
437
|
+
throw new Error("LazyCompute classes must be defined at top-level.");
|
|
438
|
+
}
|
|
439
|
+
const skcompute = this.refs.binding.SkipRuntime_createLazyCompute(
|
|
440
|
+
this.refs.handles.register(computeObj),
|
|
441
|
+
);
|
|
442
|
+
const lazyCollection =
|
|
443
|
+
this.refs.binding.SkipRuntime_Context__createLazyCollection(skcompute);
|
|
444
|
+
return new LazyCollectionImpl<K, V>(lazyCollection, this.refs);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
useExternalResource<K extends Json, V extends Json>(resource: {
|
|
448
|
+
service: string;
|
|
449
|
+
identifier: string;
|
|
450
|
+
params?: { [param: string]: string | number };
|
|
451
|
+
}): EagerCollection<K, V> {
|
|
452
|
+
const collection =
|
|
453
|
+
this.refs.binding.SkipRuntime_Context__useExternalResource(
|
|
454
|
+
resource.service,
|
|
455
|
+
resource.identifier,
|
|
456
|
+
this.refs.skjson.exportJSON(resource.params ?? {}),
|
|
457
|
+
);
|
|
458
|
+
return new EagerCollectionImpl<K, V>(collection, this.refs);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
jsonExtract(value: JsonObject, pattern: string): Json[] {
|
|
462
|
+
return this.refs.skjson.importJSON(
|
|
463
|
+
this.refs.binding.SkipRuntime_Context__jsonExtract(
|
|
464
|
+
this.refs.skjson.exportJSON(value),
|
|
465
|
+
pattern,
|
|
466
|
+
),
|
|
467
|
+
) as Json[];
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export class ServiceInstanceFactory {
|
|
472
|
+
constructor(private init: (service: SkipService) => ServiceInstance) {}
|
|
473
|
+
|
|
474
|
+
initService(service: SkipService): ServiceInstance {
|
|
475
|
+
return this.init(service);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export type GetResult<T> = {
|
|
480
|
+
request?: string;
|
|
481
|
+
payload: T;
|
|
482
|
+
errors: Json[];
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
export type Executor<T> = {
|
|
486
|
+
resolve: (value: T) => void;
|
|
487
|
+
reject: (reason?: any) => void;
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
class AllChecker<K extends Json, V extends Json> implements Checker {
|
|
491
|
+
constructor(
|
|
492
|
+
private readonly service: ServiceInstance,
|
|
493
|
+
private readonly executor: Executor<Entry<K, V>[]>,
|
|
494
|
+
private readonly resource: string,
|
|
495
|
+
private readonly params: { [param: string]: string },
|
|
496
|
+
) {}
|
|
497
|
+
|
|
498
|
+
check(request: string): void {
|
|
499
|
+
const result = this.service.getAll<K, V>(
|
|
500
|
+
this.resource,
|
|
501
|
+
this.params,
|
|
502
|
+
request,
|
|
503
|
+
);
|
|
504
|
+
if (result.errors.length > 0) {
|
|
505
|
+
this.executor.reject(new Error(JSON.stringify(result.errors)));
|
|
506
|
+
} else {
|
|
507
|
+
this.executor.resolve(result.payload);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
class OneChecker<K extends Json, V extends Json> implements Checker {
|
|
513
|
+
constructor(
|
|
514
|
+
private readonly service: ServiceInstance,
|
|
515
|
+
private readonly executor: Executor<V[]>,
|
|
516
|
+
private readonly resource: string,
|
|
517
|
+
private readonly params: { [param: string]: string },
|
|
518
|
+
private readonly key: K,
|
|
519
|
+
) {}
|
|
520
|
+
|
|
521
|
+
check(request: string): void {
|
|
522
|
+
const result = this.service.getArray<K, V>(
|
|
523
|
+
this.resource,
|
|
524
|
+
this.key,
|
|
525
|
+
this.params,
|
|
526
|
+
request,
|
|
527
|
+
);
|
|
528
|
+
if (result.errors.length > 0) {
|
|
529
|
+
this.executor.reject(new Error(JSON.stringify(result.errors)));
|
|
530
|
+
} else {
|
|
531
|
+
this.executor.resolve(result.payload);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* A `ServiceInstance` is a running instance of a `SkipService`, providing access to its resources
|
|
538
|
+
* and operations to manage susbscriptions and the service itself.
|
|
539
|
+
*/
|
|
540
|
+
export class ServiceInstance {
|
|
541
|
+
constructor(private readonly refs: Refs) {}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Instantiate a resource with some parameters and client session authentication token
|
|
545
|
+
* @param identifier - The resource instance identifier
|
|
546
|
+
* @param resource - A resource name, which must correspond to a key in this `SkipService`'s `resources` field
|
|
547
|
+
* @param params - Resource parameters, which will be passed to the resource constructor specified in this `SkipService`'s `resources` field
|
|
548
|
+
*/
|
|
549
|
+
instantiateResource(
|
|
550
|
+
identifier: string,
|
|
551
|
+
resource: string,
|
|
552
|
+
params: { [param: string]: string },
|
|
553
|
+
): void {
|
|
554
|
+
const errorHdl = this.refs.runWithGC(() => {
|
|
555
|
+
return this.refs.binding.SkipRuntime_Runtime__createResource(
|
|
556
|
+
identifier,
|
|
557
|
+
resource,
|
|
558
|
+
this.refs.skjson.exportJSON(params),
|
|
559
|
+
);
|
|
560
|
+
});
|
|
561
|
+
if (errorHdl) throw this.refs.handles.deleteHandle(errorHdl);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Creates if not exists and get all current values of specified resource
|
|
566
|
+
* @param resource - the resource name corresponding to a key in remotes field of SkipService
|
|
567
|
+
* @param params - the parameters of the resource used to build the resource with the corresponding constructor specified in remotes field of SkipService
|
|
568
|
+
* @returns The current values of the corresponding resource with reactive responce token to allow subscription
|
|
569
|
+
*/
|
|
570
|
+
getAll<K extends Json, V extends Json>(
|
|
571
|
+
resource: string,
|
|
572
|
+
params: { [param: string]: string } = {},
|
|
573
|
+
request?: string | Executor<Entry<K, V>[]>,
|
|
574
|
+
): GetResult<Entry<K, V>[]> {
|
|
575
|
+
const get_ = () => {
|
|
576
|
+
return this.refs.skjson.importJSON(
|
|
577
|
+
this.refs.binding.SkipRuntime_Runtime__getAll(
|
|
578
|
+
resource,
|
|
579
|
+
this.refs.skjson.exportJSON(params),
|
|
580
|
+
request !== undefined
|
|
581
|
+
? typeof request == "string"
|
|
582
|
+
? this.refs.binding.SkipRuntime_createIdentifier(request)
|
|
583
|
+
: this.refs.binding.SkipRuntime_createChecker(
|
|
584
|
+
this.refs.handles.register(
|
|
585
|
+
new AllChecker(this, request, resource, params),
|
|
586
|
+
),
|
|
587
|
+
)
|
|
588
|
+
: null,
|
|
589
|
+
),
|
|
590
|
+
true,
|
|
591
|
+
);
|
|
592
|
+
};
|
|
593
|
+
const result = this.refs.needGC() ? this.refs.runWithGC(get_) : get_();
|
|
594
|
+
if (typeof result == "number")
|
|
595
|
+
throw this.refs.handles.deleteHandle(result as Handle<Error>);
|
|
596
|
+
return result as GetResult<Entry<K, V>[]>;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Get the current value of a key in the specified resource instance, creating it if it doesn't already exist
|
|
601
|
+
* @param resource - A resource name, which must correspond to a key in this `SkipService`'s `resources` field
|
|
602
|
+
* @param key - A key to look up in the resource instance
|
|
603
|
+
* @param params - Resource parameters, passed to the resource constructor specified in this `SkipService`'s `resources` field
|
|
604
|
+
* @returns The current value(s) for this key in the specified resource instance
|
|
605
|
+
*/
|
|
606
|
+
getArray<K extends Json, V extends Json>(
|
|
607
|
+
resource: string,
|
|
608
|
+
key: K,
|
|
609
|
+
params: { [param: string]: string } = {},
|
|
610
|
+
request?: string | Executor<V[]>,
|
|
611
|
+
): GetResult<V[]> {
|
|
612
|
+
const get_ = () => {
|
|
613
|
+
return this.refs.skjson.importJSON(
|
|
614
|
+
this.refs.binding.SkipRuntime_Runtime__getForKey(
|
|
615
|
+
resource,
|
|
616
|
+
this.refs.skjson.exportJSON(params),
|
|
617
|
+
this.refs.skjson.exportJSON(key),
|
|
618
|
+
request !== undefined
|
|
619
|
+
? typeof request == "string"
|
|
620
|
+
? this.refs.binding.SkipRuntime_createIdentifier(request)
|
|
621
|
+
: this.refs.binding.SkipRuntime_createChecker(
|
|
622
|
+
this.refs.handles.register(
|
|
623
|
+
new OneChecker(this, request, resource, params, key),
|
|
624
|
+
),
|
|
625
|
+
)
|
|
626
|
+
: null,
|
|
627
|
+
),
|
|
628
|
+
true,
|
|
629
|
+
);
|
|
630
|
+
};
|
|
631
|
+
const needGC = this.refs.needGC();
|
|
632
|
+
const result = needGC ? this.refs.runWithGC(get_) : get_();
|
|
633
|
+
if (typeof result == "number")
|
|
634
|
+
throw this.refs.handles.deleteHandle(result as Handle<Error>);
|
|
635
|
+
return result as GetResult<V[]>;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Close the specified resource instance
|
|
640
|
+
* @param resourceInstanceId - The resource identifier
|
|
641
|
+
*/
|
|
642
|
+
closeResourceInstance(resourceInstanceId: string): void {
|
|
643
|
+
const errorHdl = this.refs.runWithGC(() => {
|
|
644
|
+
return this.refs.binding.SkipRuntime_Runtime__closeResource(
|
|
645
|
+
resourceInstanceId,
|
|
646
|
+
);
|
|
647
|
+
});
|
|
648
|
+
if (errorHdl) throw this.refs.handles.deleteHandle(errorHdl);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Initiate reactive subscription on a resource instance
|
|
653
|
+
* @param resourceInstanceId - the resource instance identifier
|
|
654
|
+
* @param notifier - the object containing subscription callbacks
|
|
655
|
+
* @param notifier.subscribed - A callback to execute when subscription effectivly done
|
|
656
|
+
* @param notifier.notify - A callback to execute on collection updates
|
|
657
|
+
* @param notifier.close - A callback to execute on resource close
|
|
658
|
+
* @param watermark - the watermark where to start the subscription
|
|
659
|
+
* @returns A subcription identifier
|
|
660
|
+
*/
|
|
661
|
+
subscribe<K extends Json, V extends Json>(
|
|
662
|
+
resourceInstanceId: string,
|
|
663
|
+
notifier: {
|
|
664
|
+
subscribed: () => void;
|
|
665
|
+
notify: (update: CollectionUpdate<K, V>) => void;
|
|
666
|
+
close: () => void;
|
|
667
|
+
},
|
|
668
|
+
watermark?: string,
|
|
669
|
+
): SubscriptionID {
|
|
670
|
+
const session = this.refs.runWithGC(() => {
|
|
671
|
+
const sknotifier = this.refs.binding.SkipRuntime_createNotifier(
|
|
672
|
+
this.refs.handles.register(notifier),
|
|
673
|
+
);
|
|
674
|
+
return this.refs.binding.SkipRuntime_Runtime__subscribe(
|
|
675
|
+
resourceInstanceId,
|
|
676
|
+
sknotifier,
|
|
677
|
+
watermark ?? null,
|
|
678
|
+
);
|
|
679
|
+
});
|
|
680
|
+
if (session == -1n) {
|
|
681
|
+
throw new UnknownCollectionError(
|
|
682
|
+
`Unknown resource instance '${resourceInstanceId}'`,
|
|
683
|
+
);
|
|
684
|
+
} else if (session < 0n) {
|
|
685
|
+
throw new Error("Unknown error");
|
|
686
|
+
}
|
|
687
|
+
return session as SubscriptionID;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Terminate a client's subscription to a reactive resource instance
|
|
692
|
+
* @param id - The subcription identifier returned by a call to `subscribe`
|
|
693
|
+
*/
|
|
694
|
+
unsubscribe(id: SubscriptionID): void {
|
|
695
|
+
const errorHdl = this.refs.runWithGC(() => {
|
|
696
|
+
return this.refs.binding.SkipRuntime_Runtime__unsubscribe(id);
|
|
697
|
+
});
|
|
698
|
+
if (errorHdl) {
|
|
699
|
+
throw this.refs.handles.deleteHandle(errorHdl);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Update an input collection
|
|
705
|
+
* @param collection - the name of the input collection to update
|
|
706
|
+
* @param entries - entries to update in the collection.
|
|
707
|
+
*/
|
|
708
|
+
update<K extends Json, V extends Json>(
|
|
709
|
+
collection: string,
|
|
710
|
+
entries: Entry<K, V>[],
|
|
711
|
+
): void {
|
|
712
|
+
const errorHdl = this.refs.runWithGC(() => {
|
|
713
|
+
return this.refs.binding.SkipRuntime_Runtime__update(
|
|
714
|
+
collection,
|
|
715
|
+
this.refs.skjson.exportJSON(entries),
|
|
716
|
+
);
|
|
717
|
+
});
|
|
718
|
+
if (errorHdl) {
|
|
719
|
+
throw this.refs.handles.deleteHandle(errorHdl);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Close all resources and shut down the service.
|
|
725
|
+
* Any subsequent calls on the service will result in errors.
|
|
726
|
+
*/
|
|
727
|
+
close(): void {
|
|
728
|
+
const errorHdl = this.refs.runWithGC(() => {
|
|
729
|
+
return this.refs.binding.SkipRuntime_closeService();
|
|
730
|
+
});
|
|
731
|
+
if (errorHdl) {
|
|
732
|
+
throw this.refs.handles.deleteHandle(errorHdl);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
export class NonEmptyIteratorImpl<T> implements NonEmptyIterator<T> {
|
|
738
|
+
constructor(
|
|
739
|
+
private readonly skjson: JsonConverter,
|
|
740
|
+
private readonly binding: FromBinding,
|
|
741
|
+
private readonly pointer: Pointer<Internal.NonEmptyIterator>,
|
|
742
|
+
) {
|
|
743
|
+
this.skjson = skjson;
|
|
744
|
+
this.binding = binding;
|
|
745
|
+
this.pointer = pointer;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
next(): Nullable<T & Param> {
|
|
749
|
+
return this.skjson.importOptJSON(
|
|
750
|
+
this.binding.SkipRuntime_NonEmptyIterator__next(this.pointer),
|
|
751
|
+
) as Nullable<T & Param>;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
getUnique(): T & Param {
|
|
755
|
+
const value = this.skjson.importOptJSON(
|
|
756
|
+
this.binding.SkipRuntime_NonEmptyIterator__uniqueValue(this.pointer),
|
|
757
|
+
) as Nullable<T & Param>;
|
|
758
|
+
if (value == null) throw new NonUniqueValueException();
|
|
759
|
+
return value;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
toArray: () => (T & Param)[] = () => {
|
|
763
|
+
return Array.from(this);
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
[Symbol.iterator](): Iterator<T & Param> {
|
|
767
|
+
const cloned_iter = new NonEmptyIteratorImpl<T & Param>(
|
|
768
|
+
this.skjson,
|
|
769
|
+
this.binding,
|
|
770
|
+
this.binding.SkipRuntime_NonEmptyIterator__clone(this.pointer),
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
return {
|
|
774
|
+
next() {
|
|
775
|
+
const value = cloned_iter.next();
|
|
776
|
+
return { value, done: value == null } as IteratorResult<T & Param>;
|
|
777
|
+
},
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
map<U>(f: (value: T & Param, index: number) => U, thisObj?: any): U[] {
|
|
782
|
+
return this.toArray().map(f, thisObj);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
export class ToBinding {
|
|
787
|
+
private readonly stack: Stack;
|
|
788
|
+
private readonly handles: Handles;
|
|
789
|
+
private skjson?: JsonConverter;
|
|
790
|
+
|
|
791
|
+
constructor(
|
|
792
|
+
private binding: FromBinding,
|
|
793
|
+
private runWithGC: <T>(fn: () => T) => T,
|
|
794
|
+
private getConverter: () => JsonConverter,
|
|
795
|
+
private getError: (skExc: Pointer<Internal.Exception>) => Error,
|
|
796
|
+
) {
|
|
797
|
+
this.stack = new Stack();
|
|
798
|
+
this.handles = new Handles();
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
register<T>(v: T): Handle<T> {
|
|
802
|
+
return this.handles.register(v);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
deleteHandle<T>(id: Handle<T>): T {
|
|
806
|
+
return this.handles.deleteHandle(id);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
SkipRuntime_getErrorHdl(exn: Pointer<Internal.Exception>): Handle<Error> {
|
|
810
|
+
return this.handles.register(this.getError(exn));
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
SkipRuntime_pushContext(context: Pointer<Internal.Context>): void {
|
|
814
|
+
this.stack.push(context);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
SkipRuntime_popContext(): void {
|
|
818
|
+
this.stack.pop();
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
SkipRuntime_getContext(): Nullable<Pointer<Internal.Context>> {
|
|
822
|
+
return this.stack.get();
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Mapper
|
|
826
|
+
|
|
827
|
+
SkipRuntime_Mapper__mapEntry(
|
|
828
|
+
skmapper: Handle<JSONMapper>,
|
|
829
|
+
key: Pointer<Internal.CJSON>,
|
|
830
|
+
values: Pointer<Internal.NonEmptyIterator>,
|
|
831
|
+
): Pointer<Internal.CJArray> {
|
|
832
|
+
const skjson = this.getJsonConverter();
|
|
833
|
+
const mapper = this.handles.get(skmapper);
|
|
834
|
+
const result = mapper.mapEntry(
|
|
835
|
+
skjson.importJSON(key) as Json,
|
|
836
|
+
new NonEmptyIteratorImpl<Json>(skjson, this.binding, values),
|
|
837
|
+
);
|
|
838
|
+
return skjson.exportJSON(Array.from(result) as [[Json, Json]]);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
SkipRuntime_deleteMapper(mapper: Handle<JSONMapper>): void {
|
|
842
|
+
this.handles.deleteHandle(mapper);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// LazyCompute
|
|
846
|
+
|
|
847
|
+
SkipRuntime_LazyCompute__compute(
|
|
848
|
+
sklazyCompute: Handle<JSONLazyCompute>,
|
|
849
|
+
self: string,
|
|
850
|
+
skkey: Pointer<Internal.CJSON>,
|
|
851
|
+
): Pointer<Internal.CJArray> {
|
|
852
|
+
const skjson = this.getJsonConverter();
|
|
853
|
+
const lazyCompute = this.handles.get(sklazyCompute);
|
|
854
|
+
const computed = lazyCompute.compute(
|
|
855
|
+
new LazyCollectionImpl<Json, Json>(self, this.refs()),
|
|
856
|
+
skjson.importJSON(skkey) as Json,
|
|
857
|
+
);
|
|
858
|
+
return skjson.exportJSON(computed ? [computed] : []);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
SkipRuntime_deleteLazyCompute(lazyCompute: Handle<JSONLazyCompute>): void {
|
|
862
|
+
this.handles.deleteHandle(lazyCompute);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// Resource
|
|
866
|
+
|
|
867
|
+
SkipRuntime_Resource__instantiate(
|
|
868
|
+
skresource: Handle<Resource>,
|
|
869
|
+
skcollections: Pointer<Internal.CJObject>,
|
|
870
|
+
): string {
|
|
871
|
+
const skjson = this.getJsonConverter();
|
|
872
|
+
const resource = this.handles.get(skresource);
|
|
873
|
+
const collections: NamedCollections = {};
|
|
874
|
+
const keysIds = skjson.importJSON(skcollections) as {
|
|
875
|
+
[key: string]: string;
|
|
876
|
+
};
|
|
877
|
+
const refs = this.refs();
|
|
878
|
+
for (const [key, name] of Object.entries(keysIds)) {
|
|
879
|
+
collections[key] = new EagerCollectionImpl<Json, Json>(name, refs);
|
|
880
|
+
}
|
|
881
|
+
const collection = resource.instantiate(collections, new ContextImpl(refs));
|
|
882
|
+
return (collection as EagerCollectionImpl<Json, Json>).collection;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
SkipRuntime_deleteResource(resource: Handle<Resource>): void {
|
|
886
|
+
this.handles.deleteHandle(resource);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// ResourceBuilder
|
|
890
|
+
|
|
891
|
+
SkipRuntime_ResourceBuilder__build(
|
|
892
|
+
skbuilder: Handle<ResourceBuilder>,
|
|
893
|
+
skparams: Pointer<Internal.CJObject>,
|
|
894
|
+
): Pointer<Internal.Resource> {
|
|
895
|
+
const skjson = this.getJsonConverter();
|
|
896
|
+
const builder = this.handles.get(skbuilder);
|
|
897
|
+
const resource = builder.build(
|
|
898
|
+
skjson.importJSON(skparams) as { [param: string]: string },
|
|
899
|
+
);
|
|
900
|
+
return this.binding.SkipRuntime_createResource(
|
|
901
|
+
this.handles.register(resource),
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
SkipRuntime_deleteResourceBuilder(builder: Handle<ResourceBuilder>): void {
|
|
906
|
+
this.handles.deleteHandle(builder);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// Service
|
|
910
|
+
|
|
911
|
+
SkipRuntime_Service__createGraph(
|
|
912
|
+
skservice: Handle<SkipService>,
|
|
913
|
+
skcollections: Pointer<Internal.CJObject>,
|
|
914
|
+
): Pointer<Internal.CJObject> {
|
|
915
|
+
const skjson = this.getJsonConverter();
|
|
916
|
+
const service = this.handles.get(skservice);
|
|
917
|
+
const collections: NamedCollections = {};
|
|
918
|
+
const keysIds = skjson.importJSON(skcollections) as {
|
|
919
|
+
[key: string]: string;
|
|
920
|
+
};
|
|
921
|
+
const refs = this.refs();
|
|
922
|
+
for (const [key, name] of Object.entries(keysIds)) {
|
|
923
|
+
collections[key] = new EagerCollectionImpl<Json, Json>(name, refs);
|
|
924
|
+
}
|
|
925
|
+
const result = service.createGraph(collections, new ContextImpl(refs));
|
|
926
|
+
const collectionsNames: { [name: string]: string } = {};
|
|
927
|
+
for (const [name, collection] of Object.entries(result)) {
|
|
928
|
+
collectionsNames[name] = (
|
|
929
|
+
collection as EagerCollectionImpl<Json, Json>
|
|
930
|
+
).collection;
|
|
931
|
+
}
|
|
932
|
+
return skjson.exportJSON(collectionsNames);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
SkipRuntime_deleteService(service: Handle<SkipService>): void {
|
|
936
|
+
this.handles.deleteHandle(service);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// Notifier
|
|
940
|
+
|
|
941
|
+
SkipRuntime_Notifier__subscribed<K extends Json, V extends Json>(
|
|
942
|
+
sknotifier: Handle<Notifier<K, V>>,
|
|
943
|
+
): void {
|
|
944
|
+
const notifier = this.handles.get(sknotifier);
|
|
945
|
+
notifier.subscribed();
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
SkipRuntime_Notifier__notify<K extends Json, V extends Json>(
|
|
949
|
+
sknotifier: Handle<Notifier<K, V>>,
|
|
950
|
+
skvalues: Pointer<Internal.CJArray<Internal.CJArray<Internal.CJSON>>>,
|
|
951
|
+
watermark: Watermark,
|
|
952
|
+
isUpdates: number,
|
|
953
|
+
) {
|
|
954
|
+
const skjson = this.getJsonConverter();
|
|
955
|
+
const notifier = this.handles.get(sknotifier);
|
|
956
|
+
const values = skjson.importJSON(skvalues, true) as Entry<K, V>[];
|
|
957
|
+
const isInitial = isUpdates ? false : true;
|
|
958
|
+
notifier.notify({
|
|
959
|
+
values,
|
|
960
|
+
watermark,
|
|
961
|
+
isInitial,
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
SkipRuntime_Notifier__close<K extends Json, V extends Json>(
|
|
966
|
+
sknotifier: Handle<Notifier<K, V>>,
|
|
967
|
+
): void {
|
|
968
|
+
const notifier = this.handles.get(sknotifier);
|
|
969
|
+
notifier.close();
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
SkipRuntime_deleteNotifier<K extends Json, V extends Json>(
|
|
973
|
+
notifier: Handle<Notifier<K, V>>,
|
|
974
|
+
): void {
|
|
975
|
+
this.handles.deleteHandle(notifier);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// Reducer
|
|
979
|
+
|
|
980
|
+
SkipRuntime_Reducer__add(
|
|
981
|
+
skreducer: Handle<Reducer<Json, Json>>,
|
|
982
|
+
skacc: Nullable<Pointer<Internal.CJSON>>,
|
|
983
|
+
skvalue: Pointer<Internal.CJSON>,
|
|
984
|
+
): Pointer<Internal.CJSON> {
|
|
985
|
+
const skjson = this.getJsonConverter();
|
|
986
|
+
const reducer = this.handles.get(skreducer);
|
|
987
|
+
return skjson.exportJSON(
|
|
988
|
+
reducer.add(
|
|
989
|
+
skacc ? (skjson.importJSON(skacc) as Json) : null,
|
|
990
|
+
skjson.importJSON(skvalue) as Json & Param,
|
|
991
|
+
),
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
SkipRuntime_Reducer__remove(
|
|
996
|
+
skreducer: Handle<Reducer<Json, Json>>,
|
|
997
|
+
skacc: Pointer<Internal.CJSON>,
|
|
998
|
+
skvalue: Pointer<Internal.CJSON>,
|
|
999
|
+
): Nullable<Pointer<Internal.CJSON>> {
|
|
1000
|
+
const skjson = this.getJsonConverter();
|
|
1001
|
+
const reducer = this.handles.get(skreducer);
|
|
1002
|
+
return skjson.exportJSON(
|
|
1003
|
+
reducer.remove(
|
|
1004
|
+
skjson.importJSON(skacc) as Json,
|
|
1005
|
+
skjson.importJSON(skvalue) as Json & Param,
|
|
1006
|
+
),
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
SkipRuntime_deleteReducer(reducer: Handle<Reducer<Json, Json>>): void {
|
|
1011
|
+
this.handles.deleteHandle(reducer);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// ExternalService
|
|
1015
|
+
|
|
1016
|
+
SkipRuntime_ExternalService__subscribe(
|
|
1017
|
+
sksupplier: Handle<ExternalService>,
|
|
1018
|
+
writerId: string,
|
|
1019
|
+
resource: string,
|
|
1020
|
+
skparams: Pointer<Internal.CJObject>,
|
|
1021
|
+
): void {
|
|
1022
|
+
const skjson = this.getJsonConverter();
|
|
1023
|
+
const supplier = this.handles.get(sksupplier);
|
|
1024
|
+
const writer = new CollectionWriter(writerId, this.refs());
|
|
1025
|
+
const params = skjson.importJSON(skparams, true) as {
|
|
1026
|
+
[param: string]: string;
|
|
1027
|
+
};
|
|
1028
|
+
supplier.subscribe(resource, params, {
|
|
1029
|
+
update: writer.update.bind(writer),
|
|
1030
|
+
error: writer.error.bind(writer),
|
|
1031
|
+
loading: writer.loading.bind(writer),
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
SkipRuntime_ExternalService__unsubscribe(
|
|
1036
|
+
sksupplier: Handle<ExternalService>,
|
|
1037
|
+
resource: string,
|
|
1038
|
+
skparams: Pointer<Internal.CJObject>,
|
|
1039
|
+
): void {
|
|
1040
|
+
const skjson = this.getJsonConverter();
|
|
1041
|
+
const supplier = this.handles.get(sksupplier);
|
|
1042
|
+
const params = skjson.importJSON(skparams, true) as {
|
|
1043
|
+
[param: string]: string;
|
|
1044
|
+
};
|
|
1045
|
+
supplier.unsubscribe(resource, params);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
SkipRuntime_ExternalService__shutdown(
|
|
1049
|
+
sksupplier: Handle<ExternalService>,
|
|
1050
|
+
): void {
|
|
1051
|
+
const supplier = this.handles.get(sksupplier);
|
|
1052
|
+
supplier.shutdown();
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
SkipRuntime_deleteExternalService(supplier: Handle<ExternalService>): void {
|
|
1056
|
+
this.handles.deleteHandle(supplier);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// Checker
|
|
1060
|
+
|
|
1061
|
+
SkipRuntime_Checker__check(
|
|
1062
|
+
skchecker: Handle<Checker>,
|
|
1063
|
+
request: string,
|
|
1064
|
+
): void {
|
|
1065
|
+
const checker = this.handles.get(skchecker);
|
|
1066
|
+
checker.check(request);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
SkipRuntime_deleteChecker(checker: Handle<Checker>): void {
|
|
1070
|
+
this.handles.deleteHandle(checker);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
initService(service: SkipService): ServiceInstance {
|
|
1074
|
+
const refs = this.refs();
|
|
1075
|
+
const errorHdl = refs.runWithGC(() => {
|
|
1076
|
+
const skExternalServices =
|
|
1077
|
+
refs.binding.SkipRuntime_ExternalServiceMap__create();
|
|
1078
|
+
if (service.externalServices) {
|
|
1079
|
+
for (const [name, remote] of Object.entries(service.externalServices)) {
|
|
1080
|
+
const skremote = refs.binding.SkipRuntime_createExternalService(
|
|
1081
|
+
refs.handles.register(remote),
|
|
1082
|
+
);
|
|
1083
|
+
refs.binding.SkipRuntime_ExternalServiceMap__add(
|
|
1084
|
+
skExternalServices,
|
|
1085
|
+
name,
|
|
1086
|
+
skremote,
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
const skresources = refs.binding.SkipRuntime_ResourceBuilderMap__create();
|
|
1091
|
+
for (const [name, builder] of Object.entries(service.resources)) {
|
|
1092
|
+
const skbuilder = refs.binding.SkipRuntime_createResourceBuilder(
|
|
1093
|
+
refs.handles.register(new ResourceBuilder(builder)),
|
|
1094
|
+
);
|
|
1095
|
+
refs.binding.SkipRuntime_ResourceBuilderMap__add(
|
|
1096
|
+
skresources,
|
|
1097
|
+
name,
|
|
1098
|
+
skbuilder,
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
const skservice = refs.binding.SkipRuntime_createService(
|
|
1102
|
+
refs.handles.register(service),
|
|
1103
|
+
refs.skjson.exportJSON(service.initialData ?? {}),
|
|
1104
|
+
skresources,
|
|
1105
|
+
skExternalServices,
|
|
1106
|
+
);
|
|
1107
|
+
return refs.binding.SkipRuntime_initService(skservice);
|
|
1108
|
+
});
|
|
1109
|
+
if (errorHdl) throw refs.handles.deleteHandle(errorHdl);
|
|
1110
|
+
return new ServiceInstance(refs);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
//
|
|
1114
|
+
private getJsonConverter() {
|
|
1115
|
+
if (this.skjson == undefined) {
|
|
1116
|
+
this.skjson = this.getConverter();
|
|
1117
|
+
}
|
|
1118
|
+
return this.skjson;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
private needGC() {
|
|
1122
|
+
return this.SkipRuntime_getContext() == null;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
private refs(): Refs {
|
|
1126
|
+
return new Refs(
|
|
1127
|
+
this.binding,
|
|
1128
|
+
this.getConverter(),
|
|
1129
|
+
this.handles,
|
|
1130
|
+
this.needGC.bind(this),
|
|
1131
|
+
this.runWithGC,
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
}
|