@cosystem/core 0.0.0 → 1.0.0
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/index.d.mts +141 -12
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +997 -95
- package/dist/index.mjs.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +7 -2
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { create, createReactiveTracker } from "coaction";
|
|
1
|
+
import { computed as computed$1, create, createReactiveTracker } from "coaction";
|
|
2
2
|
//#region src/errors.ts
|
|
3
3
|
var CosystemError = class extends Error {
|
|
4
4
|
constructor(message) {
|
|
@@ -184,7 +184,7 @@ var RuntimeContainer = class RuntimeContainer {
|
|
|
184
184
|
if (record === void 0) return;
|
|
185
185
|
const context = this.createResolutionContext("sync");
|
|
186
186
|
const value = this.resolveRecord(record, context);
|
|
187
|
-
if (isPromiseLike(value)) throw new AsyncProviderInSyncResolutionError(record.tokenName);
|
|
187
|
+
if (isPromiseLike$1(value)) throw new AsyncProviderInSyncResolutionError(record.tokenName);
|
|
188
188
|
return value;
|
|
189
189
|
}
|
|
190
190
|
getAll(token) {
|
|
@@ -192,7 +192,7 @@ var RuntimeContainer = class RuntimeContainer {
|
|
|
192
192
|
const context = this.createResolutionContext("sync");
|
|
193
193
|
return records.map((record) => {
|
|
194
194
|
const value = this.resolveRecord(record, context);
|
|
195
|
-
if (isPromiseLike(value)) throw new AsyncProviderInSyncResolutionError(record.tokenName);
|
|
195
|
+
if (isPromiseLike$1(value)) throw new AsyncProviderInSyncResolutionError(record.tokenName);
|
|
196
196
|
return value;
|
|
197
197
|
});
|
|
198
198
|
}
|
|
@@ -227,7 +227,13 @@ var RuntimeContainer = class RuntimeContainer {
|
|
|
227
227
|
const deps = options.deps ?? target.inject ?? [];
|
|
228
228
|
const context = this.createResolutionContext("sync");
|
|
229
229
|
const values = this.resolveDependencies(deps, context);
|
|
230
|
-
if (isPromiseLike(values)) throw new AsyncProviderInSyncResolutionError(target.name);
|
|
230
|
+
if (isPromiseLike$1(values)) throw new AsyncProviderInSyncResolutionError(target.name);
|
|
231
|
+
return Reflect.construct(target, values);
|
|
232
|
+
}
|
|
233
|
+
async buildAsync(target, options = {}) {
|
|
234
|
+
const deps = options.deps ?? target.inject ?? [];
|
|
235
|
+
const context = this.createResolutionContext("async");
|
|
236
|
+
const values = await this.resolveDependencies(deps, context);
|
|
231
237
|
return Reflect.construct(target, values);
|
|
232
238
|
}
|
|
233
239
|
freeze() {
|
|
@@ -301,7 +307,7 @@ var RuntimeContainer = class RuntimeContainer {
|
|
|
301
307
|
};
|
|
302
308
|
try {
|
|
303
309
|
const value = this.createValue(record, context);
|
|
304
|
-
if (isPromiseLike(value)) {
|
|
310
|
+
if (isPromiseLike$1(value)) {
|
|
305
311
|
if (context.mode === "sync") throw new AsyncProviderInSyncResolutionError(record.tokenName);
|
|
306
312
|
return Promise.resolve(value).then(finalize, fail);
|
|
307
313
|
}
|
|
@@ -315,14 +321,14 @@ var RuntimeContainer = class RuntimeContainer {
|
|
|
315
321
|
case "class": {
|
|
316
322
|
const provider = record.provider;
|
|
317
323
|
const deps = this.resolveDependencies(record.deps, context);
|
|
318
|
-
if (isPromiseLike(deps)) return Promise.resolve(deps).then((values) => Reflect.construct(provider.useClass, values));
|
|
324
|
+
if (isPromiseLike$1(deps)) return Promise.resolve(deps).then((values) => Reflect.construct(provider.useClass, values));
|
|
319
325
|
return Reflect.construct(provider.useClass, deps);
|
|
320
326
|
}
|
|
321
327
|
case "value": return record.provider.useValue;
|
|
322
328
|
case "factory": {
|
|
323
329
|
const provider = record.provider;
|
|
324
330
|
const deps = this.resolveDependencies(record.deps, context);
|
|
325
|
-
if (isPromiseLike(deps)) return Promise.resolve(deps).then((values) => runWithInjectContext(context, () => provider.useFactory(...values)));
|
|
331
|
+
if (isPromiseLike$1(deps)) return Promise.resolve(deps).then((values) => runWithInjectContext(context, () => provider.useFactory(...values)));
|
|
326
332
|
return runWithInjectContext(context, () => provider.useFactory(...deps));
|
|
327
333
|
}
|
|
328
334
|
case "existing": return this.resolveDependency(record.provider.useExisting, context);
|
|
@@ -333,7 +339,7 @@ var RuntimeContainer = class RuntimeContainer {
|
|
|
333
339
|
const asyncValues = [];
|
|
334
340
|
for (const [index, dep] of deps.entries()) {
|
|
335
341
|
const value = this.resolveDependency(dep, context);
|
|
336
|
-
if (isPromiseLike(value)) asyncValues.push(Promise.resolve(value).then((resolved) => {
|
|
342
|
+
if (isPromiseLike$1(value)) asyncValues.push(Promise.resolve(value).then((resolved) => {
|
|
337
343
|
values[index] = resolved;
|
|
338
344
|
return resolved;
|
|
339
345
|
}));
|
|
@@ -348,16 +354,21 @@ var RuntimeContainer = class RuntimeContainer {
|
|
|
348
354
|
const records = this.getAllRecords(dep.token);
|
|
349
355
|
return this.resolveAllRecords(records, context);
|
|
350
356
|
}
|
|
351
|
-
const record = dep.optional === true ? this.getSingleRecord(dep.token, true) : this.
|
|
357
|
+
const record = dep.optional === true ? this.getSingleRecord(dep.token, true) : this.getRequiredRecord(dep.token, context);
|
|
352
358
|
if (record === void 0) return;
|
|
353
359
|
return this.resolveRecord(record, context);
|
|
354
360
|
}
|
|
355
|
-
const record = this.
|
|
361
|
+
const record = this.getRequiredRecord(dep, context);
|
|
356
362
|
return this.resolveRecord(record, context);
|
|
357
363
|
}
|
|
364
|
+
getRequiredRecord(token, context) {
|
|
365
|
+
const record = this.getSingleRecord(token, true);
|
|
366
|
+
if (record === void 0) throw new MissingProviderError(tokenName(token), context.stack.map((entry) => entry.tokenName));
|
|
367
|
+
return record;
|
|
368
|
+
}
|
|
358
369
|
resolveAllRecords(records, context) {
|
|
359
370
|
const values = records.map((record) => this.resolveRecord(record, context));
|
|
360
|
-
if (values.some(isPromiseLike)) return Promise.all(values);
|
|
371
|
+
if (values.some(isPromiseLike$1)) return Promise.all(values);
|
|
361
372
|
return values;
|
|
362
373
|
}
|
|
363
374
|
getCached(record, context) {
|
|
@@ -410,7 +421,7 @@ var RuntimeContainer = class RuntimeContainer {
|
|
|
410
421
|
function isDependencyObject(dep) {
|
|
411
422
|
return typeof dep === "object" && dep !== null && "token" in dep;
|
|
412
423
|
}
|
|
413
|
-
function isPromiseLike(value) {
|
|
424
|
+
function isPromiseLike$1(value) {
|
|
414
425
|
return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
|
|
415
426
|
}
|
|
416
427
|
function isLifetimeLeak(parentScope, childScope) {
|
|
@@ -437,18 +448,69 @@ async function disposeValue(value, record) {
|
|
|
437
448
|
if (typeof maybeDisposable.destroy === "function") await maybeDisposable.destroy();
|
|
438
449
|
}
|
|
439
450
|
//#endregion
|
|
451
|
+
//#region src/lazyModule.ts
|
|
452
|
+
function lazyModule(load) {
|
|
453
|
+
return {
|
|
454
|
+
kind: "lazyModule",
|
|
455
|
+
load
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function isLazyModule(input) {
|
|
459
|
+
return typeof input === "object" && input !== null && "kind" in input && input.kind === "lazyModule" && "load" in input && typeof input.load === "function";
|
|
460
|
+
}
|
|
461
|
+
function normalizeLazyModuleProviders(input) {
|
|
462
|
+
if (isProviderInput(input)) return [input];
|
|
463
|
+
if (Array.isArray(input)) {
|
|
464
|
+
assertProviderArray(input);
|
|
465
|
+
return input;
|
|
466
|
+
}
|
|
467
|
+
if (typeof input === "object" && input !== null) {
|
|
468
|
+
const exports = input;
|
|
469
|
+
const providers = [];
|
|
470
|
+
if (exports.providers !== void 0) {
|
|
471
|
+
assertProviderArray(exports.providers);
|
|
472
|
+
providers.push(...exports.providers);
|
|
473
|
+
}
|
|
474
|
+
if (exports.default !== void 0) if (Array.isArray(exports.default)) {
|
|
475
|
+
assertProviderArray(exports.default);
|
|
476
|
+
providers.push(...exports.default);
|
|
477
|
+
} else if (isProviderInput(exports.default)) providers.push(exports.default);
|
|
478
|
+
else throw new CosystemError("Lazy module default export must be a provider or provider array.");
|
|
479
|
+
if (providers.length > 0) return providers;
|
|
480
|
+
}
|
|
481
|
+
throw new CosystemError("Lazy module loader must return a provider, provider array, or module exports with providers/default.");
|
|
482
|
+
}
|
|
483
|
+
function assertProviderArray(values) {
|
|
484
|
+
if (values.every(isProviderInput)) return;
|
|
485
|
+
throw new CosystemError("Lazy module provider arrays may only contain provider entries.");
|
|
486
|
+
}
|
|
487
|
+
function isProviderInput(value) {
|
|
488
|
+
if (typeof value === "function") return true;
|
|
489
|
+
return typeof value === "object" && value !== null && "provide" in value && ("useClass" in value || "useValue" in value || "useFactory" in value || "useExisting" in value);
|
|
490
|
+
}
|
|
491
|
+
//#endregion
|
|
440
492
|
//#region src/metadata.ts
|
|
441
493
|
const moduleMetadata = /* @__PURE__ */ new WeakMap();
|
|
442
|
-
|
|
494
|
+
const moduleMetadataKey = Symbol.for("@cosystem/core/moduleMetadata");
|
|
495
|
+
const symbolMetadataKey = Symbol.metadata;
|
|
496
|
+
function defineModule(target, options = {}, context) {
|
|
443
497
|
const metadata = ensureModuleMetadata(target);
|
|
498
|
+
mergeModuleMetadata(metadata, readContextMetadata(context));
|
|
444
499
|
applyModuleOptions(metadata, options);
|
|
445
500
|
addProperties(metadata.state, options.state);
|
|
446
501
|
addProperties(metadata.actions, options.actions);
|
|
447
502
|
addProperties(metadata.computed, options.computed);
|
|
503
|
+
addProperties(metadata.effects, options.effects);
|
|
504
|
+
writeContextMetadata(context, metadata);
|
|
448
505
|
return target;
|
|
449
506
|
}
|
|
450
507
|
function getModuleMetadata(target) {
|
|
451
|
-
|
|
508
|
+
const existing = moduleMetadata.get(target);
|
|
509
|
+
if (existing !== void 0) return existing;
|
|
510
|
+
const metadata = readSymbolMetadata(target);
|
|
511
|
+
if (metadata === void 0) return;
|
|
512
|
+
moduleMetadata.set(target, metadata);
|
|
513
|
+
return metadata;
|
|
452
514
|
}
|
|
453
515
|
function addModuleState(target, property) {
|
|
454
516
|
ensureModuleMetadata(target).state.add(property);
|
|
@@ -459,6 +521,9 @@ function addModuleAction(target, property) {
|
|
|
459
521
|
function addModuleComputed(target, property) {
|
|
460
522
|
ensureModuleMetadata(target).computed.add(property);
|
|
461
523
|
}
|
|
524
|
+
function addModuleEffect(target, property) {
|
|
525
|
+
ensureModuleMetadata(target).effects.add(property);
|
|
526
|
+
}
|
|
462
527
|
function applyModuleOptions(metadata, options) {
|
|
463
528
|
if (options.name !== void 0) metadata.name = options.name;
|
|
464
529
|
if (options.deps !== void 0) metadata.deps = options.deps;
|
|
@@ -471,38 +536,96 @@ function ensureModuleMetadata(target) {
|
|
|
471
536
|
kind: "module",
|
|
472
537
|
state: /* @__PURE__ */ new Set(),
|
|
473
538
|
actions: /* @__PURE__ */ new Set(),
|
|
474
|
-
computed: /* @__PURE__ */ new Set()
|
|
539
|
+
computed: /* @__PURE__ */ new Set(),
|
|
540
|
+
effects: /* @__PURE__ */ new Set()
|
|
475
541
|
};
|
|
476
542
|
moduleMetadata.set(target, metadata);
|
|
477
543
|
return metadata;
|
|
478
544
|
}
|
|
545
|
+
function ensureContextModuleMetadata(context) {
|
|
546
|
+
if (context?.metadata === void 0) return;
|
|
547
|
+
const existing = readContextMetadata(context);
|
|
548
|
+
if (existing !== void 0) return existing;
|
|
549
|
+
const metadata = createModuleMetadata();
|
|
550
|
+
writeContextMetadata(context, metadata);
|
|
551
|
+
return metadata;
|
|
552
|
+
}
|
|
479
553
|
function addProperties(target, properties) {
|
|
480
554
|
for (const property of properties ?? []) target.add(property);
|
|
481
555
|
}
|
|
556
|
+
function createModuleMetadata() {
|
|
557
|
+
return {
|
|
558
|
+
kind: "module",
|
|
559
|
+
state: /* @__PURE__ */ new Set(),
|
|
560
|
+
actions: /* @__PURE__ */ new Set(),
|
|
561
|
+
computed: /* @__PURE__ */ new Set(),
|
|
562
|
+
effects: /* @__PURE__ */ new Set()
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function readContextMetadata(context) {
|
|
566
|
+
const value = context?.metadata?.[moduleMetadataKey];
|
|
567
|
+
return isModuleMetadata(value) ? value : void 0;
|
|
568
|
+
}
|
|
569
|
+
function writeContextMetadata(context, metadata) {
|
|
570
|
+
if (context?.metadata === void 0) return;
|
|
571
|
+
context.metadata[moduleMetadataKey] = metadata;
|
|
572
|
+
}
|
|
573
|
+
function readSymbolMetadata(target) {
|
|
574
|
+
if (symbolMetadataKey === void 0) return;
|
|
575
|
+
const metadata = target[symbolMetadataKey];
|
|
576
|
+
if (metadata === void 0 || metadata === null || typeof metadata !== "object") return;
|
|
577
|
+
const value = metadata[moduleMetadataKey];
|
|
578
|
+
return isModuleMetadata(value) ? value : void 0;
|
|
579
|
+
}
|
|
580
|
+
function mergeModuleMetadata(target, source) {
|
|
581
|
+
if (source === void 0 || source === target) return;
|
|
582
|
+
applyModuleOptions(target, source);
|
|
583
|
+
addProperties(target.state, [...source.state]);
|
|
584
|
+
addProperties(target.actions, [...source.actions]);
|
|
585
|
+
addProperties(target.computed, [...source.computed]);
|
|
586
|
+
addProperties(target.effects, [...source.effects]);
|
|
587
|
+
}
|
|
588
|
+
function isModuleMetadata(value) {
|
|
589
|
+
return typeof value === "object" && value !== null && value.kind === "module" && value instanceof Object && value.state instanceof Set && value.actions instanceof Set && value.computed instanceof Set && value.effects instanceof Set;
|
|
590
|
+
}
|
|
482
591
|
//#endregion
|
|
483
592
|
//#region src/app.ts
|
|
593
|
+
const runtimeModuleMetadataKey = Symbol.for("@cosystem/core/runtimeModule");
|
|
594
|
+
const appContainerMap = /* @__PURE__ */ new WeakMap();
|
|
484
595
|
function createApp(options = {}) {
|
|
485
596
|
return createAppInternal(options);
|
|
486
597
|
}
|
|
598
|
+
function runInAction(module, callback, options) {
|
|
599
|
+
const metadata = getRuntimeModuleMetadata(module);
|
|
600
|
+
if (metadata === void 0) throw new CosystemError("runInAction() target is not a CoSystem module instance.");
|
|
601
|
+
return metadata.app.runInAction(module, callback, options);
|
|
602
|
+
}
|
|
487
603
|
function createAppInternal(options = {}) {
|
|
488
|
-
const parent = isApp(options.parent) ? options.parent
|
|
604
|
+
const parent = isApp(options.parent) ? getAppContainer(options.parent) : options.parent;
|
|
489
605
|
const container = parent === void 0 ? createContainer() : createContainer({ parent });
|
|
490
606
|
const moduleTokens = [];
|
|
607
|
+
const lazyModules = [];
|
|
491
608
|
for (const provider of options.providers ?? []) {
|
|
609
|
+
if (isLazyModule(provider)) {
|
|
610
|
+
lazyModules.push(provider);
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
492
613
|
const normalized = normalizeAppProvider(provider);
|
|
493
614
|
container.provide(normalized.provider);
|
|
494
615
|
if (normalized.moduleToken !== void 0) moduleTokens.push(normalized.moduleToken);
|
|
495
616
|
}
|
|
496
|
-
for (const override of options.overrides ?? [])
|
|
617
|
+
for (const override of options.overrides ?? []) {
|
|
618
|
+
const normalized = normalizeAppProvider(override);
|
|
619
|
+
if (normalized.moduleToken !== void 0 && !moduleTokens.some((moduleToken) => moduleToken === normalized.moduleToken)) throw new CosystemError(`Cannot add ${tokenName(normalized.moduleToken)} as a new CoSystem module through overrides.`);
|
|
620
|
+
container.override(normalized.provider);
|
|
621
|
+
}
|
|
497
622
|
container.freeze();
|
|
498
623
|
const modules = instantiateModules(container, moduleTokens);
|
|
499
|
-
const store = create(createRootState(modules),
|
|
500
|
-
name: "cosystem",
|
|
501
|
-
sliceMode: "single"
|
|
502
|
-
});
|
|
624
|
+
const store = create(createRootState(modules), createStoreOptions(options.engine, shouldEnablePatches(options)));
|
|
503
625
|
const app = new RuntimeApp({
|
|
504
626
|
container,
|
|
505
627
|
devOptions: options.devOptions ?? {},
|
|
628
|
+
lazyModules,
|
|
506
629
|
modules,
|
|
507
630
|
plugins: options.plugins ?? [],
|
|
508
631
|
state: { version: 0 },
|
|
@@ -510,26 +633,35 @@ function createAppInternal(options = {}) {
|
|
|
510
633
|
...options.testInspector === void 0 ? {} : { testInspector: options.testInspector }
|
|
511
634
|
});
|
|
512
635
|
app.bindModules();
|
|
636
|
+
app.attachRuntimeMetadata();
|
|
637
|
+
instantiateEagerProviders(container);
|
|
513
638
|
app.runModuleCreatedHooks();
|
|
514
639
|
app.init();
|
|
640
|
+
appContainerMap.set(app, container);
|
|
515
641
|
return app;
|
|
516
642
|
}
|
|
517
643
|
var RuntimeApp = class {
|
|
518
|
-
container;
|
|
519
644
|
state;
|
|
520
645
|
store;
|
|
646
|
+
#container;
|
|
521
647
|
devOptions;
|
|
522
648
|
modules;
|
|
649
|
+
pendingLazyModules;
|
|
523
650
|
moduleByToken = /* @__PURE__ */ new Map();
|
|
524
651
|
moduleByName = /* @__PURE__ */ new Map();
|
|
525
652
|
plugins;
|
|
526
653
|
testInspector;
|
|
654
|
+
effectDisposers = [];
|
|
655
|
+
pendingEffects = /* @__PURE__ */ new Set();
|
|
656
|
+
loadedLazyModules = /* @__PURE__ */ new WeakMap();
|
|
657
|
+
dynamicScopes = [];
|
|
527
658
|
initPromise = Promise.resolve();
|
|
528
659
|
isStarted = false;
|
|
529
660
|
isDisposed = false;
|
|
530
661
|
constructor(options) {
|
|
531
|
-
this
|
|
662
|
+
this.#container = options.container;
|
|
532
663
|
this.devOptions = options.devOptions;
|
|
664
|
+
this.pendingLazyModules = [...options.lazyModules];
|
|
533
665
|
this.modules = options.modules;
|
|
534
666
|
this.plugins = options.plugins;
|
|
535
667
|
this.state = options.state;
|
|
@@ -539,29 +671,35 @@ var RuntimeApp = class {
|
|
|
539
671
|
this.moduleByToken.set(moduleBinding.token, moduleBinding);
|
|
540
672
|
this.moduleByName.set(moduleBinding.name, moduleBinding);
|
|
541
673
|
}
|
|
674
|
+
this.wrapStoreSetState();
|
|
542
675
|
this.store.subscribe(() => {
|
|
543
676
|
this.state.version += 1;
|
|
544
677
|
const state = this.store.getPureState();
|
|
545
|
-
this.testInspector?.
|
|
678
|
+
this.testInspector?.recordState(state);
|
|
546
679
|
this.emitStateChange({ state });
|
|
547
680
|
});
|
|
681
|
+
this.testInspector?.setFlushEffects(() => this.flushEffects());
|
|
548
682
|
}
|
|
549
683
|
get started() {
|
|
550
684
|
return this.isStarted;
|
|
551
685
|
}
|
|
552
686
|
get(token) {
|
|
553
|
-
|
|
687
|
+
const moduleBinding = this.moduleByToken.get(token);
|
|
688
|
+
if (moduleBinding !== void 0) return moduleBinding.instance;
|
|
689
|
+
return this.#container.get(token);
|
|
554
690
|
}
|
|
555
691
|
getAsync(token) {
|
|
556
|
-
|
|
692
|
+
const moduleBinding = this.moduleByToken.get(token);
|
|
693
|
+
if (moduleBinding !== void 0) return Promise.resolve(moduleBinding.instance);
|
|
694
|
+
return this.#container.getAsync(token);
|
|
557
695
|
}
|
|
558
696
|
getAll(token) {
|
|
559
|
-
return this
|
|
697
|
+
return this.#container.getAll(token);
|
|
560
698
|
}
|
|
561
699
|
getModule(token) {
|
|
562
|
-
const
|
|
563
|
-
if (
|
|
564
|
-
return
|
|
700
|
+
const moduleBinding = this.moduleByToken.get(token);
|
|
701
|
+
if (moduleBinding === void 0) throw new CosystemError(`${tokenName(token)} is not a CoSystem module.`);
|
|
702
|
+
return moduleBinding.instance;
|
|
565
703
|
}
|
|
566
704
|
getModuleByName(name) {
|
|
567
705
|
const moduleBinding = this.moduleByName.get(name);
|
|
@@ -588,50 +726,129 @@ var RuntimeApp = class {
|
|
|
588
726
|
tracker.dispose();
|
|
589
727
|
};
|
|
590
728
|
}
|
|
729
|
+
runInAction(module, callback, options = {}) {
|
|
730
|
+
const moduleBinding = this.resolveModuleBinding(module);
|
|
731
|
+
return this.runActionCallback(moduleBinding, options.name ?? "runInAction", options.args ?? [], callback);
|
|
732
|
+
}
|
|
591
733
|
async start() {
|
|
592
734
|
if (this.isStarted) return;
|
|
593
735
|
await this.initPromise;
|
|
594
|
-
|
|
595
|
-
|
|
736
|
+
try {
|
|
737
|
+
await this.runLifecycle("onStart");
|
|
738
|
+
this.isStarted = true;
|
|
739
|
+
} catch (error) {
|
|
740
|
+
this.emitError(error, { phase: "start" });
|
|
741
|
+
throw error;
|
|
742
|
+
}
|
|
596
743
|
}
|
|
597
744
|
async stop() {
|
|
598
745
|
if (!this.isStarted) return;
|
|
599
|
-
|
|
600
|
-
|
|
746
|
+
try {
|
|
747
|
+
await this.runLifecycle("onStop", true);
|
|
748
|
+
this.isStarted = false;
|
|
749
|
+
} catch (error) {
|
|
750
|
+
this.emitError(error, { phase: "stop" });
|
|
751
|
+
throw error;
|
|
752
|
+
}
|
|
601
753
|
}
|
|
602
754
|
async dispose() {
|
|
603
755
|
if (this.isDisposed) return;
|
|
604
756
|
await this.stop();
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
757
|
+
try {
|
|
758
|
+
this.stopEffects();
|
|
759
|
+
await this.waitForPendingEffects();
|
|
760
|
+
await this.runLifecycle("onDispose", true);
|
|
761
|
+
for (const scope of this.dynamicScopes.toReversed()) await scope.dispose();
|
|
762
|
+
await Promise.all(this.plugins.map((plugin) => plugin.dispose?.()));
|
|
763
|
+
await this.#container.dispose();
|
|
764
|
+
this.store.destroy();
|
|
765
|
+
this.isDisposed = true;
|
|
766
|
+
} catch (error) {
|
|
767
|
+
this.emitError(error, { phase: "dispose" });
|
|
768
|
+
throw error;
|
|
769
|
+
}
|
|
610
770
|
}
|
|
611
771
|
createScope(options) {
|
|
612
|
-
return { container: this
|
|
772
|
+
return { container: this.#container.createScope(options) };
|
|
773
|
+
}
|
|
774
|
+
async load(module) {
|
|
775
|
+
if (module === void 0) {
|
|
776
|
+
const modules = this.pendingLazyModules.splice(0);
|
|
777
|
+
const results = [];
|
|
778
|
+
for (const pendingModule of modules) results.push(await this.load(pendingModule));
|
|
779
|
+
return results;
|
|
780
|
+
}
|
|
781
|
+
const existing = this.loadedLazyModules.get(module);
|
|
782
|
+
if (existing !== void 0) return existing;
|
|
783
|
+
if (this.isDisposed) throw new CosystemError("Cannot load a lazy module after app disposal.");
|
|
784
|
+
await this.initPromise;
|
|
785
|
+
try {
|
|
786
|
+
const providers = normalizeLazyModuleProviders(await module.load());
|
|
787
|
+
const scopeContainer = this.#container.createScope();
|
|
788
|
+
const moduleTokens = [];
|
|
789
|
+
for (const provider of providers) {
|
|
790
|
+
const normalized = normalizeAppProvider(provider);
|
|
791
|
+
scopeContainer.provide(normalized.provider);
|
|
792
|
+
if (normalized.moduleToken !== void 0) moduleTokens.push(normalized.moduleToken);
|
|
793
|
+
}
|
|
794
|
+
scopeContainer.freeze();
|
|
795
|
+
const loadedModules = instantiateModules(scopeContainer, moduleTokens, false);
|
|
796
|
+
this.assertNewModules(loadedModules);
|
|
797
|
+
this.installModuleState(loadedModules);
|
|
798
|
+
this.registerModules(loadedModules);
|
|
799
|
+
this.bindModules(loadedModules);
|
|
800
|
+
this.attachRuntimeMetadata(loadedModules);
|
|
801
|
+
instantiateEagerProviders(scopeContainer);
|
|
802
|
+
this.dynamicScopes.push(scopeContainer);
|
|
803
|
+
this.runModuleCreatedHooks(loadedModules);
|
|
804
|
+
await this.runLifecycle("onInit", false, loadedModules);
|
|
805
|
+
this.startEffects(loadedModules);
|
|
806
|
+
if (this.isStarted) await this.runLifecycle("onStart", false, loadedModules);
|
|
807
|
+
const result = {
|
|
808
|
+
modules: loadedModules.map(toModuleCreatedEvent),
|
|
809
|
+
scope: { container: scopeContainer }
|
|
810
|
+
};
|
|
811
|
+
this.loadedLazyModules.set(module, result);
|
|
812
|
+
return result;
|
|
813
|
+
} catch (error) {
|
|
814
|
+
this.emitError(error, { phase: "load" });
|
|
815
|
+
throw error;
|
|
816
|
+
}
|
|
613
817
|
}
|
|
614
|
-
bindModules() {
|
|
615
|
-
for (const moduleBinding of
|
|
818
|
+
bindModules(modules = this.modules) {
|
|
819
|
+
for (const moduleBinding of modules) {
|
|
616
820
|
this.bindState(moduleBinding);
|
|
617
821
|
this.bindComputed(moduleBinding);
|
|
618
822
|
this.bindActions(moduleBinding);
|
|
619
823
|
}
|
|
620
824
|
}
|
|
621
|
-
|
|
622
|
-
for (const moduleBinding of
|
|
623
|
-
|
|
825
|
+
attachRuntimeMetadata(modules = this.modules) {
|
|
826
|
+
for (const moduleBinding of modules) Object.defineProperty(moduleBinding.instance, runtimeModuleMetadataKey, {
|
|
827
|
+
configurable: false,
|
|
828
|
+
enumerable: false,
|
|
829
|
+
value: {
|
|
830
|
+
app: this,
|
|
624
831
|
name: moduleBinding.name,
|
|
625
|
-
token: moduleBinding.token
|
|
626
|
-
|
|
627
|
-
|
|
832
|
+
token: moduleBinding.token
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
runModuleCreatedHooks(modules = this.modules) {
|
|
837
|
+
for (const moduleBinding of modules) {
|
|
838
|
+
const event = toModuleCreatedEvent(moduleBinding);
|
|
628
839
|
for (const plugin of this.plugins) plugin.onModuleCreated?.(event);
|
|
629
840
|
}
|
|
630
841
|
}
|
|
631
842
|
init() {
|
|
632
843
|
this.initPromise = (async () => {
|
|
633
|
-
|
|
634
|
-
|
|
844
|
+
try {
|
|
845
|
+
await Promise.all(this.plugins.map((plugin) => this.runWithAppInjectContext(() => plugin.setup?.(this))));
|
|
846
|
+
await this.runLifecycle("onInit");
|
|
847
|
+
this.startEffects();
|
|
848
|
+
} catch (error) {
|
|
849
|
+
this.emitError(error, { phase: "init" });
|
|
850
|
+
throw error;
|
|
851
|
+
}
|
|
635
852
|
})();
|
|
636
853
|
}
|
|
637
854
|
bindState(moduleBinding) {
|
|
@@ -655,15 +872,21 @@ var RuntimeApp = class {
|
|
|
655
872
|
bindComputed(moduleBinding) {
|
|
656
873
|
for (const property of moduleBinding.metadata.computed) {
|
|
657
874
|
const getter = getGetter(moduleBinding.instance, property);
|
|
875
|
+
const accessor = computed$1(() => getter.call(moduleBinding.instance));
|
|
658
876
|
moduleBinding.originalComputed.set(property, getter);
|
|
877
|
+
moduleBinding.computedAccessors.set(property, accessor);
|
|
659
878
|
Object.defineProperty(moduleBinding.instance, property, {
|
|
660
879
|
configurable: true,
|
|
661
880
|
enumerable: true,
|
|
662
|
-
get: () =>
|
|
881
|
+
get: () => {
|
|
882
|
+
if (moduleBinding.activeDraft !== void 0 || !moduleBinding.reactiveSlice) return getter.call(moduleBinding.instance);
|
|
883
|
+
return accessor();
|
|
884
|
+
}
|
|
663
885
|
});
|
|
664
886
|
}
|
|
665
887
|
}
|
|
666
888
|
readModuleState(moduleBinding, property) {
|
|
889
|
+
if (!moduleBinding.reactiveSlice) return this.store.getPureState()[moduleBinding.name]?.[property];
|
|
667
890
|
return this.store.getState()[moduleBinding.name]?.[property];
|
|
668
891
|
}
|
|
669
892
|
writeModuleState(moduleBinding, property, value) {
|
|
@@ -672,6 +895,18 @@ var RuntimeApp = class {
|
|
|
672
895
|
return;
|
|
673
896
|
}
|
|
674
897
|
if (this.devOptions.strictActions === true && moduleBinding.actionDepth === 0) throw new CosystemError(`Cannot write ${moduleBinding.name}.${String(property)} outside an action.`);
|
|
898
|
+
if (!moduleBinding.reactiveSlice) {
|
|
899
|
+
const state = this.store.getPureState();
|
|
900
|
+
const slice = state[moduleBinding.name] ?? {};
|
|
901
|
+
this.store.setState({
|
|
902
|
+
...state,
|
|
903
|
+
[moduleBinding.name]: {
|
|
904
|
+
...slice,
|
|
905
|
+
[property]: value
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
675
910
|
this.store.setState((draft) => {
|
|
676
911
|
draft[moduleBinding.name] ??= {};
|
|
677
912
|
draft[moduleBinding.name][property] = value;
|
|
@@ -680,6 +915,9 @@ var RuntimeApp = class {
|
|
|
680
915
|
runAction(moduleBinding, property, args) {
|
|
681
916
|
const action = moduleBinding.originalActions.get(property);
|
|
682
917
|
if (action === void 0) throw new CosystemError(`${moduleBinding.name}.${String(property)} is not an action.`);
|
|
918
|
+
return this.runActionCallback(moduleBinding, property, args, () => action.apply(moduleBinding.instance, [...args]));
|
|
919
|
+
}
|
|
920
|
+
runActionCallback(moduleBinding, property, args, callback) {
|
|
683
921
|
const event = {
|
|
684
922
|
args,
|
|
685
923
|
method: String(property),
|
|
@@ -691,21 +929,76 @@ var RuntimeApp = class {
|
|
|
691
929
|
let error;
|
|
692
930
|
try {
|
|
693
931
|
moduleBinding.actionDepth += 1;
|
|
694
|
-
this.store.setState((draft) => {
|
|
932
|
+
if (moduleBinding.reactiveSlice) this.store.setState((draft) => {
|
|
695
933
|
const previousDraft = moduleBinding.activeDraft;
|
|
696
934
|
moduleBinding.activeDraft = draft[moduleBinding.name] ?? {};
|
|
697
935
|
draft[moduleBinding.name] = moduleBinding.activeDraft;
|
|
698
936
|
try {
|
|
699
|
-
result =
|
|
937
|
+
result = callback();
|
|
700
938
|
} finally {
|
|
701
939
|
moduleBinding.activeDraft = previousDraft;
|
|
702
940
|
}
|
|
703
941
|
});
|
|
942
|
+
else {
|
|
943
|
+
const state = this.store.getPureState();
|
|
944
|
+
const slice = state[moduleBinding.name];
|
|
945
|
+
const previousDraft = moduleBinding.activeDraft;
|
|
946
|
+
moduleBinding.activeDraft = slice === void 0 ? {} : { ...slice };
|
|
947
|
+
try {
|
|
948
|
+
result = callback();
|
|
949
|
+
this.store.setState({
|
|
950
|
+
...state,
|
|
951
|
+
[moduleBinding.name]: moduleBinding.activeDraft
|
|
952
|
+
});
|
|
953
|
+
} finally {
|
|
954
|
+
moduleBinding.activeDraft = previousDraft;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
704
957
|
} catch (caught) {
|
|
705
958
|
error = caught;
|
|
959
|
+
this.emitError(caught, { phase: "action" });
|
|
706
960
|
} finally {
|
|
707
961
|
moduleBinding.actionDepth -= 1;
|
|
708
962
|
}
|
|
963
|
+
if (error !== void 0) {
|
|
964
|
+
this.finishAction(event, error);
|
|
965
|
+
throw error;
|
|
966
|
+
}
|
|
967
|
+
if (isPromiseLike(result)) return Promise.resolve(result).then((value) => {
|
|
968
|
+
this.finishAction(event);
|
|
969
|
+
return value;
|
|
970
|
+
}, (caught) => {
|
|
971
|
+
this.emitError(caught, { phase: "action" });
|
|
972
|
+
this.finishAction(event, caught);
|
|
973
|
+
throw caught;
|
|
974
|
+
});
|
|
975
|
+
this.finishAction(event);
|
|
976
|
+
return result;
|
|
977
|
+
}
|
|
978
|
+
resolveModuleBinding(target) {
|
|
979
|
+
if (typeof target === "object" && target !== null) {
|
|
980
|
+
const metadata = getRuntimeModuleMetadata(target);
|
|
981
|
+
if (metadata !== void 0) {
|
|
982
|
+
if (metadata.app !== this) throw new CosystemError("runInAction() target belongs to another CoSystem app.");
|
|
983
|
+
const moduleBinding = this.moduleByToken.get(metadata.token);
|
|
984
|
+
if (moduleBinding !== void 0) return moduleBinding;
|
|
985
|
+
}
|
|
986
|
+
if (isTokenObject(target)) {
|
|
987
|
+
const moduleBinding = this.moduleByToken.get(target);
|
|
988
|
+
if (moduleBinding !== void 0) return moduleBinding;
|
|
989
|
+
}
|
|
990
|
+
throw new CosystemError("runInAction() target is not a CoSystem module.");
|
|
991
|
+
}
|
|
992
|
+
if (typeof target === "string") {
|
|
993
|
+
const moduleBinding = this.moduleByName.get(target) ?? this.moduleByToken.get(target);
|
|
994
|
+
if (moduleBinding !== void 0) return moduleBinding;
|
|
995
|
+
throw new CosystemError(`${target} is not a CoSystem module.`);
|
|
996
|
+
}
|
|
997
|
+
const moduleBinding = this.moduleByToken.get(target);
|
|
998
|
+
if (moduleBinding !== void 0) return moduleBinding;
|
|
999
|
+
throw new CosystemError(`${tokenName(target)} is not a CoSystem module.`);
|
|
1000
|
+
}
|
|
1001
|
+
finishAction(event, error) {
|
|
709
1002
|
const endedEvent = {
|
|
710
1003
|
...event,
|
|
711
1004
|
endedAt: Date.now(),
|
|
@@ -713,12 +1006,63 @@ var RuntimeApp = class {
|
|
|
713
1006
|
};
|
|
714
1007
|
this.testInspector?.recordAction(endedEvent);
|
|
715
1008
|
this.emitActionEnd(endedEvent);
|
|
716
|
-
if (error !== void 0) throw error;
|
|
717
|
-
return result;
|
|
718
1009
|
}
|
|
719
|
-
async runLifecycle(method, reverse = false) {
|
|
720
|
-
const
|
|
721
|
-
for (const moduleBinding of
|
|
1010
|
+
async runLifecycle(method, reverse = false, modules = this.modules) {
|
|
1011
|
+
const orderedModules = reverse ? modules.toReversed() : modules;
|
|
1012
|
+
for (const moduleBinding of orderedModules) {
|
|
1013
|
+
const lifecycle = moduleBinding.instance;
|
|
1014
|
+
await this.runWithAppInjectContext(() => lifecycle[method]?.());
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
startEffects(modules = this.modules) {
|
|
1018
|
+
for (const moduleBinding of modules) for (const property of moduleBinding.metadata.effects) this.startEffect(moduleBinding, property);
|
|
1019
|
+
}
|
|
1020
|
+
startEffect(moduleBinding, property) {
|
|
1021
|
+
const method = getMethod(moduleBinding.instance, property);
|
|
1022
|
+
const tracker = createReactiveTracker();
|
|
1023
|
+
let disposed = false;
|
|
1024
|
+
const run = () => {
|
|
1025
|
+
if (disposed) return;
|
|
1026
|
+
try {
|
|
1027
|
+
tracker.track(() => this.runEffect(moduleBinding, property, method));
|
|
1028
|
+
} catch (error) {
|
|
1029
|
+
this.emitError(error, { phase: "effect" });
|
|
1030
|
+
throw error;
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
const unsubscribe = tracker.subscribe(() => {
|
|
1034
|
+
try {
|
|
1035
|
+
run();
|
|
1036
|
+
} catch {}
|
|
1037
|
+
});
|
|
1038
|
+
run();
|
|
1039
|
+
this.effectDisposers.push(() => {
|
|
1040
|
+
disposed = true;
|
|
1041
|
+
unsubscribe();
|
|
1042
|
+
tracker.dispose();
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
runEffect(moduleBinding, property, method) {
|
|
1046
|
+
const result = this.runWithAppInjectContext(() => method.call(moduleBinding.instance));
|
|
1047
|
+
if (!isPromiseLike(result)) return;
|
|
1048
|
+
const pending = Promise.resolve(result).then(() => void 0).catch((error) => {
|
|
1049
|
+
this.emitError(error, { phase: `effect:${moduleBinding.name}.${String(property)}` });
|
|
1050
|
+
throw error;
|
|
1051
|
+
}).finally(() => {
|
|
1052
|
+
this.pendingEffects.delete(pending);
|
|
1053
|
+
});
|
|
1054
|
+
this.pendingEffects.add(pending);
|
|
1055
|
+
pending.catch(() => void 0);
|
|
1056
|
+
}
|
|
1057
|
+
stopEffects() {
|
|
1058
|
+
for (const dispose of this.effectDisposers.splice(0).toReversed()) dispose();
|
|
1059
|
+
}
|
|
1060
|
+
async flushEffects() {
|
|
1061
|
+
await this.initPromise;
|
|
1062
|
+
await this.waitForPendingEffects();
|
|
1063
|
+
}
|
|
1064
|
+
async waitForPendingEffects() {
|
|
1065
|
+
while (this.pendingEffects.size > 0) await Promise.all(this.pendingEffects);
|
|
722
1066
|
}
|
|
723
1067
|
emitActionStart(event) {
|
|
724
1068
|
for (const plugin of this.plugins) plugin.onActionStart?.(event);
|
|
@@ -729,6 +1073,59 @@ var RuntimeApp = class {
|
|
|
729
1073
|
emitStateChange(event) {
|
|
730
1074
|
for (const plugin of this.plugins) plugin.onStateChange?.(event);
|
|
731
1075
|
}
|
|
1076
|
+
emitPatch(event) {
|
|
1077
|
+
for (const plugin of this.plugins) plugin.onPatch?.(event);
|
|
1078
|
+
}
|
|
1079
|
+
emitError(error, context) {
|
|
1080
|
+
for (const plugin of this.plugins) plugin.onError?.(error, context);
|
|
1081
|
+
}
|
|
1082
|
+
runWithAppInjectContext(callback) {
|
|
1083
|
+
return runWithInjectContext({
|
|
1084
|
+
mode: "sync",
|
|
1085
|
+
requestContainer: this.#container,
|
|
1086
|
+
resolutionCache: /* @__PURE__ */ new Map(),
|
|
1087
|
+
stack: []
|
|
1088
|
+
}, callback);
|
|
1089
|
+
}
|
|
1090
|
+
wrapStoreSetState() {
|
|
1091
|
+
const originalSetState = this.store.setState.bind(this.store);
|
|
1092
|
+
this.store.setState = ((...args) => {
|
|
1093
|
+
const result = originalSetState(...args);
|
|
1094
|
+
this.recordMutationResult(result);
|
|
1095
|
+
return result;
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
recordMutationResult(result) {
|
|
1099
|
+
if (!Array.isArray(result) || result.length < 3) return;
|
|
1100
|
+
const patches = result[1];
|
|
1101
|
+
if (patches.length === 0) return;
|
|
1102
|
+
this.testInspector?.recordPatch(patches);
|
|
1103
|
+
this.emitPatch({
|
|
1104
|
+
inversePatches: result[2],
|
|
1105
|
+
patches
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
assertNewModules(modules) {
|
|
1109
|
+
for (const moduleBinding of modules) {
|
|
1110
|
+
if (this.moduleByToken.has(moduleBinding.token)) throw new DuplicateProviderError(tokenName(moduleBinding.token));
|
|
1111
|
+
if (this.moduleByName.has(moduleBinding.name)) throw new DuplicateProviderError(moduleBinding.name);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
installModuleState(modules) {
|
|
1115
|
+
if (modules.length === 0) return;
|
|
1116
|
+
const rootState = createRootState(modules);
|
|
1117
|
+
this.store.setState({
|
|
1118
|
+
...this.store.getPureState(),
|
|
1119
|
+
...rootState
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
registerModules(modules) {
|
|
1123
|
+
for (const moduleBinding of modules) {
|
|
1124
|
+
this.modules.push(moduleBinding);
|
|
1125
|
+
this.moduleByToken.set(moduleBinding.token, moduleBinding);
|
|
1126
|
+
this.moduleByName.set(moduleBinding.name, moduleBinding);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
732
1129
|
};
|
|
733
1130
|
function normalizeAppProvider(provider) {
|
|
734
1131
|
if (typeof provider === "function") {
|
|
@@ -751,6 +1148,18 @@ function normalizeAppProvider(provider) {
|
|
|
751
1148
|
}
|
|
752
1149
|
return { provider };
|
|
753
1150
|
}
|
|
1151
|
+
function createStoreOptions(engine, enablePatches) {
|
|
1152
|
+
return {
|
|
1153
|
+
name: "cosystem",
|
|
1154
|
+
sliceMode: "single",
|
|
1155
|
+
enablePatches,
|
|
1156
|
+
...engine?.transport === void 0 ? {} : { transport: engine.transport }
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
function shouldEnablePatches(options) {
|
|
1160
|
+
if (options.engine?.patches !== void 0) return options.engine.patches;
|
|
1161
|
+
return options.testInspector !== void 0 || (options.plugins ?? []).some((plugin) => plugin.onPatch !== void 0);
|
|
1162
|
+
}
|
|
754
1163
|
function createModuleClassProviderOptions(useClass, metadata) {
|
|
755
1164
|
return {
|
|
756
1165
|
useClass,
|
|
@@ -764,7 +1173,7 @@ function mergeModuleClassProviderOptions(provider, metadata) {
|
|
|
764
1173
|
..."scope" in provider || metadata.scope === void 0 ? {} : { scope: metadata.scope }
|
|
765
1174
|
};
|
|
766
1175
|
}
|
|
767
|
-
function instantiateModules(container, moduleTokens) {
|
|
1176
|
+
function instantiateModules(container, moduleTokens, reactiveSlice = true) {
|
|
768
1177
|
const modules = [];
|
|
769
1178
|
for (const moduleToken of moduleTokens) {
|
|
770
1179
|
const instance = container.get(moduleToken);
|
|
@@ -775,16 +1184,36 @@ function instantiateModules(container, moduleTokens) {
|
|
|
775
1184
|
modules.push({
|
|
776
1185
|
actionDepth: 0,
|
|
777
1186
|
activeDraft: void 0,
|
|
1187
|
+
computedAccessors: /* @__PURE__ */ new Map(),
|
|
778
1188
|
instance,
|
|
779
1189
|
metadata,
|
|
780
1190
|
name,
|
|
781
1191
|
originalActions: /* @__PURE__ */ new Map(),
|
|
782
1192
|
originalComputed: /* @__PURE__ */ new Map(),
|
|
1193
|
+
reactiveSlice,
|
|
783
1194
|
token: moduleToken
|
|
784
1195
|
});
|
|
785
1196
|
}
|
|
786
1197
|
return modules;
|
|
787
1198
|
}
|
|
1199
|
+
function toModuleCreatedEvent(moduleBinding) {
|
|
1200
|
+
return {
|
|
1201
|
+
instance: moduleBinding.instance,
|
|
1202
|
+
name: moduleBinding.name,
|
|
1203
|
+
token: moduleBinding.token
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
function instantiateEagerProviders(container) {
|
|
1207
|
+
const internalContainer = container;
|
|
1208
|
+
for (const [token, records] of internalContainer.records) {
|
|
1209
|
+
if (!records.some((record) => record.eager)) continue;
|
|
1210
|
+
if (records.some((record) => record.multi)) {
|
|
1211
|
+
container.getAll(token);
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
container.get(token);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
788
1217
|
function createRootState(modules) {
|
|
789
1218
|
const rootState = {};
|
|
790
1219
|
for (const moduleBinding of modules) {
|
|
@@ -808,6 +1237,12 @@ function getGetter(instance, property) {
|
|
|
808
1237
|
if (descriptor?.get === void 0) throw new CosystemError(`${String(property)} is not a getter.`);
|
|
809
1238
|
return descriptor.get;
|
|
810
1239
|
}
|
|
1240
|
+
function getRuntimeModuleMetadata(module) {
|
|
1241
|
+
return module[runtimeModuleMetadataKey];
|
|
1242
|
+
}
|
|
1243
|
+
function isTokenObject(value) {
|
|
1244
|
+
return "id" in value && typeof value.id === "symbol";
|
|
1245
|
+
}
|
|
811
1246
|
function getDescriptor(instance, property) {
|
|
812
1247
|
let current = instance;
|
|
813
1248
|
while (current !== null) {
|
|
@@ -819,32 +1254,71 @@ function getDescriptor(instance, property) {
|
|
|
819
1254
|
function isApp(value) {
|
|
820
1255
|
return typeof value === "object" && value !== null && "get" in value && "start" in value && "dispose" in value;
|
|
821
1256
|
}
|
|
1257
|
+
function isPromiseLike(value) {
|
|
1258
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
|
|
1259
|
+
}
|
|
1260
|
+
function getAppContainer(app) {
|
|
1261
|
+
const container = appContainerMap.get(app);
|
|
1262
|
+
if (container === void 0) throw new CosystemError("Parent app was not created by CoSystem.");
|
|
1263
|
+
return container;
|
|
1264
|
+
}
|
|
822
1265
|
//#endregion
|
|
823
1266
|
//#region src/decorators.ts
|
|
824
1267
|
function module(options = {}) {
|
|
825
|
-
return function moduleDecorator(target,
|
|
826
|
-
defineModule(target, options);
|
|
1268
|
+
return function moduleDecorator(target, context) {
|
|
1269
|
+
defineModule(target, options, context);
|
|
827
1270
|
return target;
|
|
828
1271
|
};
|
|
829
1272
|
}
|
|
830
1273
|
function state(_value, context) {
|
|
831
1274
|
if (context.kind !== "accessor") throw new TypeError("@state only supports standard accessor decorators.");
|
|
1275
|
+
ensureContextModuleMetadata(context)?.state.add(context.name);
|
|
832
1276
|
context.addInitializer(function initializeState() {
|
|
833
1277
|
addModuleState(this.constructor, context.name);
|
|
834
1278
|
});
|
|
835
1279
|
}
|
|
836
1280
|
function action(_value, context) {
|
|
837
1281
|
if (context.kind !== "method") throw new TypeError("@action only supports method decorators.");
|
|
1282
|
+
ensureContextModuleMetadata(context)?.actions.add(context.name);
|
|
838
1283
|
context.addInitializer(function initializeAction() {
|
|
839
1284
|
addModuleAction(this.constructor, context.name);
|
|
840
1285
|
});
|
|
841
1286
|
}
|
|
842
1287
|
function computed(_value, context) {
|
|
843
1288
|
if (context.kind !== "getter") throw new TypeError("@computed only supports getter decorators.");
|
|
1289
|
+
ensureContextModuleMetadata(context)?.computed.add(context.name);
|
|
844
1290
|
context.addInitializer(function initializeComputed() {
|
|
845
1291
|
addModuleComputed(this.constructor, context.name);
|
|
846
1292
|
});
|
|
847
1293
|
}
|
|
1294
|
+
function effect(_value, context) {
|
|
1295
|
+
if (context.kind !== "method") throw new TypeError("@effect only supports method decorators.");
|
|
1296
|
+
ensureContextModuleMetadata(context)?.effects.add(context.name);
|
|
1297
|
+
context.addInitializer(function initializeEffect() {
|
|
1298
|
+
addModuleEffect(this.constructor, context.name);
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
//#endregion
|
|
1302
|
+
//#region src/loggerPlugin.ts
|
|
1303
|
+
function createLoggerPlugin(options = {}) {
|
|
1304
|
+
const logger = options.logger ?? console;
|
|
1305
|
+
return {
|
|
1306
|
+
name: "cosystem:logger",
|
|
1307
|
+
onActionEnd(event) {
|
|
1308
|
+
if (event.error !== void 0) {
|
|
1309
|
+
logger.error(`Action failed: ${event.module}.${event.method}`, event);
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
logger.info(`Action completed: ${event.module}.${event.method}`, event);
|
|
1313
|
+
},
|
|
1314
|
+
onError(error, context) {
|
|
1315
|
+
logger.error(`Runtime error during ${context.phase}`, error);
|
|
1316
|
+
},
|
|
1317
|
+
onModuleCreated(event) {
|
|
1318
|
+
logger.info(`Module created: ${event.name}`, event);
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
848
1322
|
//#endregion
|
|
849
1323
|
//#region src/testApp.ts
|
|
850
1324
|
function testApp(options = {}) {
|
|
@@ -867,9 +1341,13 @@ function testApp(options = {}) {
|
|
|
867
1341
|
if (autoStart === true) return app.start().then(() => app);
|
|
868
1342
|
return app;
|
|
869
1343
|
}
|
|
1344
|
+
function defaultFlushEffects() {
|
|
1345
|
+
return Promise.resolve();
|
|
1346
|
+
}
|
|
870
1347
|
function createTestInspector() {
|
|
871
1348
|
const actions = [];
|
|
872
1349
|
const patches = [];
|
|
1350
|
+
let flushEffects = defaultFlushEffects;
|
|
873
1351
|
let lastState;
|
|
874
1352
|
return {
|
|
875
1353
|
clearActions() {
|
|
@@ -879,7 +1357,7 @@ function createTestInspector() {
|
|
|
879
1357
|
patches.length = 0;
|
|
880
1358
|
},
|
|
881
1359
|
flushEffects() {
|
|
882
|
-
return
|
|
1360
|
+
return flushEffects();
|
|
883
1361
|
},
|
|
884
1362
|
getActions() {
|
|
885
1363
|
return actions;
|
|
@@ -894,59 +1372,78 @@ function createTestInspector() {
|
|
|
894
1372
|
actions.push(event);
|
|
895
1373
|
},
|
|
896
1374
|
recordPatch(patch) {
|
|
897
|
-
lastState = patch;
|
|
898
1375
|
patches.push(patch);
|
|
1376
|
+
},
|
|
1377
|
+
recordState(state) {
|
|
1378
|
+
lastState = state;
|
|
1379
|
+
},
|
|
1380
|
+
setFlushEffects(callback) {
|
|
1381
|
+
flushEffects = callback;
|
|
899
1382
|
}
|
|
900
1383
|
};
|
|
901
1384
|
}
|
|
902
1385
|
//#endregion
|
|
903
1386
|
//#region src/worker.ts
|
|
904
1387
|
function createWorkerApp(options) {
|
|
905
|
-
const { transport, ...appOptions } = options;
|
|
906
|
-
|
|
1388
|
+
const { stateSections, sync = "snapshot", transport, ...appOptions } = options;
|
|
1389
|
+
let stateSyncVersion = 0;
|
|
1390
|
+
let publishPatches = false;
|
|
1391
|
+
const patchPlugin = {
|
|
1392
|
+
name: "cosystem:worker-patches",
|
|
1393
|
+
onPatch(event) {
|
|
1394
|
+
if (publishPatches) {
|
|
1395
|
+
const version = stateSections === void 0 ? app.state.version : stateSyncVersion + 1;
|
|
1396
|
+
if (publishState(app, transport, event.patches, sync, stateSections, version)) stateSyncVersion = version;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
const app = createApp({
|
|
1401
|
+
...appOptions,
|
|
1402
|
+
engine: {
|
|
1403
|
+
...appOptions.engine,
|
|
1404
|
+
patches: true
|
|
1405
|
+
},
|
|
1406
|
+
plugins: [...appOptions.plugins ?? [], patchPlugin]
|
|
1407
|
+
});
|
|
1408
|
+
const ready = app.start().then(() => {
|
|
1409
|
+
publishPatches = true;
|
|
1410
|
+
transport.post({ type: "ready" });
|
|
1411
|
+
const version = stateSections === void 0 ? app.state.version : stateSyncVersion;
|
|
1412
|
+
if (publishState(app, transport, [], "snapshot", stateSections, version)) stateSyncVersion = version;
|
|
1413
|
+
});
|
|
907
1414
|
const unsubscribeTransport = transport.subscribe((message) => {
|
|
908
1415
|
if (message.type !== "call") return;
|
|
909
|
-
handleCall(app, transport, message);
|
|
910
|
-
});
|
|
911
|
-
const unsubscribeStore = app.store.subscribe(() => {
|
|
912
|
-
publishState(app, transport);
|
|
1416
|
+
handleCall(app, transport, message, ready);
|
|
913
1417
|
});
|
|
914
|
-
transport.post({ type: "ready" });
|
|
915
|
-
publishState(app, transport);
|
|
916
1418
|
return {
|
|
917
1419
|
app,
|
|
1420
|
+
ready,
|
|
918
1421
|
async dispose() {
|
|
919
1422
|
unsubscribeTransport();
|
|
920
|
-
|
|
1423
|
+
await ready.catch(() => void 0);
|
|
921
1424
|
await app.dispose();
|
|
922
1425
|
}
|
|
923
1426
|
};
|
|
924
1427
|
}
|
|
925
1428
|
function createWorkerClient(options) {
|
|
926
|
-
const { transport } = options;
|
|
1429
|
+
const { onConflict, transport } = options;
|
|
927
1430
|
const listeners = /* @__PURE__ */ new Set();
|
|
1431
|
+
const selectorWatchers = /* @__PURE__ */ new Set();
|
|
928
1432
|
const pending = /* @__PURE__ */ new Map();
|
|
929
1433
|
const state = { version: 0 };
|
|
930
1434
|
let nextId = 1;
|
|
931
1435
|
let snapshot;
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
}
|
|
939
|
-
if (message.type !== "result") return;
|
|
940
|
-
const entry = pending.get(message.id);
|
|
941
|
-
if (entry === void 0) return;
|
|
942
|
-
pending.delete(message.id);
|
|
943
|
-
if (message.error !== void 0) {
|
|
944
|
-
entry.reject(createRemoteError(message.error));
|
|
945
|
-
return;
|
|
946
|
-
}
|
|
947
|
-
entry.resolve(message.value);
|
|
1436
|
+
let readySettled = false;
|
|
1437
|
+
let resolveReady;
|
|
1438
|
+
let rejectReady;
|
|
1439
|
+
const ready = new Promise((resolve, reject) => {
|
|
1440
|
+
resolveReady = resolve;
|
|
1441
|
+
rejectReady = reject;
|
|
948
1442
|
});
|
|
1443
|
+
ready.catch(() => void 0);
|
|
1444
|
+
let unsubscribe = noop;
|
|
949
1445
|
const client = {
|
|
1446
|
+
ready,
|
|
950
1447
|
state,
|
|
951
1448
|
call(module, method, ...args) {
|
|
952
1449
|
const id = nextId;
|
|
@@ -967,9 +1464,14 @@ function createWorkerClient(options) {
|
|
|
967
1464
|
},
|
|
968
1465
|
dispose() {
|
|
969
1466
|
unsubscribe();
|
|
1467
|
+
if (!readySettled) {
|
|
1468
|
+
readySettled = true;
|
|
1469
|
+
rejectReady(new CosystemError("Worker client disposed before initial state."));
|
|
1470
|
+
}
|
|
970
1471
|
for (const entry of pending.values()) entry.reject(new CosystemError("Worker client disposed before response."));
|
|
971
1472
|
pending.clear();
|
|
972
1473
|
listeners.clear();
|
|
1474
|
+
selectorWatchers.clear();
|
|
973
1475
|
},
|
|
974
1476
|
getState() {
|
|
975
1477
|
return snapshot;
|
|
@@ -980,22 +1482,256 @@ function createWorkerClient(options) {
|
|
|
980
1482
|
return (...args) => client.call(name, property, ...args);
|
|
981
1483
|
} });
|
|
982
1484
|
},
|
|
1485
|
+
select(selector) {
|
|
1486
|
+
if (snapshot === void 0) throw new CosystemError("Worker client state is not ready.");
|
|
1487
|
+
return selector(snapshot, client);
|
|
1488
|
+
},
|
|
983
1489
|
subscribe(listener) {
|
|
984
1490
|
listeners.add(listener);
|
|
985
1491
|
return () => {
|
|
986
1492
|
listeners.delete(listener);
|
|
987
1493
|
};
|
|
1494
|
+
},
|
|
1495
|
+
watch(selector, listener, watchOptions = {}) {
|
|
1496
|
+
const watcher = {
|
|
1497
|
+
equals: watchOptions.equals ?? Object.is,
|
|
1498
|
+
immediate: watchOptions.immediate ?? false,
|
|
1499
|
+
initialized: false,
|
|
1500
|
+
listener,
|
|
1501
|
+
previous: void 0,
|
|
1502
|
+
selector
|
|
1503
|
+
};
|
|
1504
|
+
if (snapshot !== void 0) {
|
|
1505
|
+
const value = selector(snapshot, client);
|
|
1506
|
+
watcher.initialized = true;
|
|
1507
|
+
watcher.previous = value;
|
|
1508
|
+
if (watcher.immediate) listener(value, value);
|
|
1509
|
+
}
|
|
1510
|
+
selectorWatchers.add(watcher);
|
|
1511
|
+
return () => {
|
|
1512
|
+
selectorWatchers.delete(watcher);
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
const publishSelectorWatchers = () => {
|
|
1517
|
+
if (snapshot === void 0) return;
|
|
1518
|
+
for (const watcher of selectorWatchers) {
|
|
1519
|
+
const value = watcher.selector(snapshot, client);
|
|
1520
|
+
if (!watcher.initialized) {
|
|
1521
|
+
watcher.initialized = true;
|
|
1522
|
+
watcher.previous = value;
|
|
1523
|
+
if (watcher.immediate) watcher.listener(value, value);
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1526
|
+
const previous = watcher.previous;
|
|
1527
|
+
if (watcher.equals(value, previous)) continue;
|
|
1528
|
+
watcher.previous = value;
|
|
1529
|
+
watcher.listener(value, previous);
|
|
988
1530
|
}
|
|
989
1531
|
};
|
|
1532
|
+
unsubscribe = transport.subscribe((message) => {
|
|
1533
|
+
if (message.type === "state") {
|
|
1534
|
+
if (readySettled && message.version <= state.version) {
|
|
1535
|
+
reportWorkerConflict(onConflict, {
|
|
1536
|
+
currentVersion: state.version,
|
|
1537
|
+
incomingVersion: message.version,
|
|
1538
|
+
message,
|
|
1539
|
+
reason: "stale-message"
|
|
1540
|
+
});
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
const isPatchOnly = message.state === void 0 && message.sync === "patch";
|
|
1544
|
+
if (isPatchOnly && snapshot === void 0) {
|
|
1545
|
+
reportWorkerConflict(onConflict, {
|
|
1546
|
+
currentVersion: state.version,
|
|
1547
|
+
incomingVersion: message.version,
|
|
1548
|
+
message,
|
|
1549
|
+
reason: "missing-snapshot"
|
|
1550
|
+
});
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
if (isPatchOnly && readySettled && message.version !== state.version + 1) {
|
|
1554
|
+
reportWorkerConflict(onConflict, {
|
|
1555
|
+
currentVersion: state.version,
|
|
1556
|
+
incomingVersion: message.version,
|
|
1557
|
+
message,
|
|
1558
|
+
reason: "version-gap"
|
|
1559
|
+
});
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
try {
|
|
1563
|
+
snapshot = isPatchOnly ? applyWorkerPatches(snapshot, message.patches ?? []) : message.state;
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
reportWorkerConflict(onConflict, {
|
|
1566
|
+
currentVersion: state.version,
|
|
1567
|
+
error,
|
|
1568
|
+
incomingVersion: message.version,
|
|
1569
|
+
message,
|
|
1570
|
+
reason: "patch-apply-failed"
|
|
1571
|
+
});
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
state.version = message.version;
|
|
1575
|
+
if (!readySettled) {
|
|
1576
|
+
readySettled = true;
|
|
1577
|
+
resolveReady();
|
|
1578
|
+
}
|
|
1579
|
+
for (const listener of listeners) listener(message);
|
|
1580
|
+
publishSelectorWatchers();
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
if (message.type !== "result") return;
|
|
1584
|
+
const entry = pending.get(message.id);
|
|
1585
|
+
if (entry === void 0) return;
|
|
1586
|
+
pending.delete(message.id);
|
|
1587
|
+
if (message.error !== void 0) {
|
|
1588
|
+
entry.reject(createRemoteError(message.error));
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
entry.resolve(message.value);
|
|
1592
|
+
});
|
|
990
1593
|
return client;
|
|
991
1594
|
}
|
|
1595
|
+
function reportWorkerConflict(onConflict, event) {
|
|
1596
|
+
onConflict?.(event);
|
|
1597
|
+
}
|
|
992
1598
|
function createMemoryWorkerTransportPair() {
|
|
993
1599
|
const leftListeners = /* @__PURE__ */ new Set();
|
|
994
1600
|
const rightListeners = /* @__PURE__ */ new Set();
|
|
995
1601
|
return [createMemoryWorkerTransport(leftListeners, rightListeners), createMemoryWorkerTransport(rightListeners, leftListeners)];
|
|
996
1602
|
}
|
|
997
|
-
|
|
1603
|
+
function createPostMessageWorkerTransport(endpoint, options = {}) {
|
|
1604
|
+
const source = options.source ?? endpoint;
|
|
1605
|
+
const target = options.target ?? endpoint;
|
|
1606
|
+
return {
|
|
1607
|
+
post(message) {
|
|
1608
|
+
try {
|
|
1609
|
+
target.postMessage(message);
|
|
1610
|
+
} catch (error) {
|
|
1611
|
+
options.onError?.(error, message);
|
|
1612
|
+
}
|
|
1613
|
+
},
|
|
1614
|
+
subscribe(listener) {
|
|
1615
|
+
const handleMessage = (event) => {
|
|
1616
|
+
if (!isWorkerMessage(event.data)) return;
|
|
1617
|
+
listener(event.data);
|
|
1618
|
+
};
|
|
1619
|
+
source.addEventListener("message", handleMessage);
|
|
1620
|
+
return () => {
|
|
1621
|
+
source.removeEventListener("message", handleMessage);
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
function createBroadcastWorkerTransport(broadcast, options = {}) {
|
|
1627
|
+
const channel = options.channel ?? defaultBroadcastWorkerChannel;
|
|
1628
|
+
const peerId = options.peerId ?? createWorkerPeerId();
|
|
1629
|
+
const callRoutes = /* @__PURE__ */ new Map();
|
|
1630
|
+
let nextRoutedCallId = 1;
|
|
1631
|
+
return {
|
|
1632
|
+
post(message) {
|
|
1633
|
+
try {
|
|
1634
|
+
const routed = routeBroadcastWorkerMessage(message, callRoutes);
|
|
1635
|
+
const target = routed.target ?? getBroadcastWorkerTarget(message, options);
|
|
1636
|
+
const envelope = {
|
|
1637
|
+
channel,
|
|
1638
|
+
message: routed.message,
|
|
1639
|
+
source: peerId,
|
|
1640
|
+
type: "cosystem:worker",
|
|
1641
|
+
...target === void 0 ? {} : { target }
|
|
1642
|
+
};
|
|
1643
|
+
broadcast.postMessage(envelope);
|
|
1644
|
+
} catch (error) {
|
|
1645
|
+
options.onError?.(error, message);
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
subscribe(listener) {
|
|
1649
|
+
const handleMessage = (event) => {
|
|
1650
|
+
const envelope = event.data;
|
|
1651
|
+
if (!isBroadcastWorkerMessageEnvelope(envelope) || envelope.channel !== channel || envelope.source === peerId || envelope.target !== void 0 && envelope.target !== peerId) return;
|
|
1652
|
+
if (envelope.message.type === "call") {
|
|
1653
|
+
const routedCallId = nextRoutedCallId;
|
|
1654
|
+
nextRoutedCallId += 1;
|
|
1655
|
+
callRoutes.set(routedCallId, {
|
|
1656
|
+
id: envelope.message.id,
|
|
1657
|
+
source: envelope.source
|
|
1658
|
+
});
|
|
1659
|
+
listener({
|
|
1660
|
+
...envelope.message,
|
|
1661
|
+
id: routedCallId
|
|
1662
|
+
});
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
listener(envelope.message);
|
|
1666
|
+
};
|
|
1667
|
+
broadcast.addEventListener("message", handleMessage);
|
|
1668
|
+
return () => {
|
|
1669
|
+
broadcast.removeEventListener("message", handleMessage);
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
function createMemoryBroadcastChannel(name = defaultBroadcastWorkerChannel) {
|
|
1675
|
+
return new MemoryBroadcastChannel(name);
|
|
1676
|
+
}
|
|
1677
|
+
function createDataTransportWorkerTransport(dataTransport, options = {}) {
|
|
1678
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1679
|
+
const disposers = [];
|
|
1680
|
+
let listening = false;
|
|
1681
|
+
const start = () => {
|
|
1682
|
+
if (listening) return;
|
|
1683
|
+
listening = true;
|
|
1684
|
+
for (const type of workerMessageTypes) {
|
|
1685
|
+
const dispose = dataTransport.listen(type, (message) => {
|
|
1686
|
+
if (!isWorkerMessage(message)) return;
|
|
1687
|
+
for (const listener of listeners) listener(message);
|
|
1688
|
+
});
|
|
1689
|
+
if (typeof dispose === "function") disposers.push(dispose);
|
|
1690
|
+
}
|
|
1691
|
+
};
|
|
1692
|
+
const stop = () => {
|
|
1693
|
+
if (!listening) return;
|
|
1694
|
+
listening = false;
|
|
1695
|
+
for (const dispose of disposers.splice(0)) dispose();
|
|
1696
|
+
};
|
|
1697
|
+
return {
|
|
1698
|
+
post(message) {
|
|
1699
|
+
dataTransport.emit({
|
|
1700
|
+
name: message.type,
|
|
1701
|
+
respond: false
|
|
1702
|
+
}, message).catch((error) => {
|
|
1703
|
+
options.onError?.(error, message);
|
|
1704
|
+
});
|
|
1705
|
+
},
|
|
1706
|
+
subscribe(listener) {
|
|
1707
|
+
start();
|
|
1708
|
+
listeners.add(listener);
|
|
1709
|
+
return () => {
|
|
1710
|
+
listeners.delete(listener);
|
|
1711
|
+
if (listeners.size === 0) stop();
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1716
|
+
function getBroadcastWorkerTarget(message, options) {
|
|
1717
|
+
return message.type === "call" ? options.targetPeerId : void 0;
|
|
1718
|
+
}
|
|
1719
|
+
function routeBroadcastWorkerMessage(message, callRoutes) {
|
|
1720
|
+
if (message.type !== "result") return { message };
|
|
1721
|
+
const route = callRoutes.get(message.id);
|
|
1722
|
+
if (route === void 0) return { message };
|
|
1723
|
+
callRoutes.delete(message.id);
|
|
1724
|
+
return {
|
|
1725
|
+
message: {
|
|
1726
|
+
...message,
|
|
1727
|
+
id: route.id
|
|
1728
|
+
},
|
|
1729
|
+
target: route.source
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
async function handleCall(app, transport, message, ready) {
|
|
998
1733
|
try {
|
|
1734
|
+
await ready;
|
|
999
1735
|
const module = app.getModuleByName(message.module);
|
|
1000
1736
|
const method = module[message.method];
|
|
1001
1737
|
if (typeof method !== "function") throw new CosystemError(`${message.module}.${message.method} is not callable.`);
|
|
@@ -1013,13 +1749,112 @@ async function handleCall(app, transport, message) {
|
|
|
1013
1749
|
});
|
|
1014
1750
|
}
|
|
1015
1751
|
}
|
|
1016
|
-
function publishState(app, transport) {
|
|
1017
|
-
|
|
1018
|
-
|
|
1752
|
+
function publishState(app, transport, patches = [], mode = "snapshot", sections, version = app.state.version) {
|
|
1753
|
+
const filteredPatches = filterWorkerPatches(patches, sections);
|
|
1754
|
+
const isPatch = filteredPatches.length > 0;
|
|
1755
|
+
if (patches.length > 0 && filteredPatches.length === 0) return false;
|
|
1756
|
+
const state = filterWorkerState(app.store.getPureState(), sections);
|
|
1757
|
+
const message = {
|
|
1758
|
+
...isPatch && mode === "patch" ? {} : { state },
|
|
1759
|
+
...sections === void 0 ? {} : { sections },
|
|
1760
|
+
sync: isPatch ? "patch" : "snapshot",
|
|
1019
1761
|
type: "state",
|
|
1020
|
-
version
|
|
1762
|
+
version,
|
|
1763
|
+
...isPatch ? { patches: filteredPatches } : {}
|
|
1764
|
+
};
|
|
1765
|
+
transport.post(message);
|
|
1766
|
+
return true;
|
|
1767
|
+
}
|
|
1768
|
+
function filterWorkerState(state, sections) {
|
|
1769
|
+
if (sections === void 0 || !isRecord(state)) return state;
|
|
1770
|
+
const filtered = {};
|
|
1771
|
+
for (const section of sections) if (section in state) filtered[section] = state[section];
|
|
1772
|
+
return filtered;
|
|
1773
|
+
}
|
|
1774
|
+
function filterWorkerPatches(patches, sections) {
|
|
1775
|
+
if (sections === void 0) return patches;
|
|
1776
|
+
const sectionSet = new Set(sections);
|
|
1777
|
+
return patches.filter((patch) => {
|
|
1778
|
+
if (!isWorkerPatch(patch)) throw new CosystemError("Worker state patch is invalid.");
|
|
1779
|
+
const section = getWorkerPatchSection(patch);
|
|
1780
|
+
return section !== void 0 && sectionSet.has(String(section));
|
|
1021
1781
|
});
|
|
1022
1782
|
}
|
|
1783
|
+
function getWorkerPatchSection(patch) {
|
|
1784
|
+
return normalizePatchPath(patch.path)[0];
|
|
1785
|
+
}
|
|
1786
|
+
function applyWorkerPatches(state, patches) {
|
|
1787
|
+
let next = state;
|
|
1788
|
+
for (const patch of patches) next = applyWorkerPatch(next, patch);
|
|
1789
|
+
return next;
|
|
1790
|
+
}
|
|
1791
|
+
function applyWorkerPatch(state, patch) {
|
|
1792
|
+
if (!isWorkerPatch(patch)) throw new CosystemError("Worker state patch is invalid.");
|
|
1793
|
+
const path = normalizePatchPath(patch.path);
|
|
1794
|
+
if (path.length === 0) {
|
|
1795
|
+
if (patch.op === "remove") return;
|
|
1796
|
+
return patch.value;
|
|
1797
|
+
}
|
|
1798
|
+
return applyPatchAtPath(state, path, patch);
|
|
1799
|
+
}
|
|
1800
|
+
function applyPatchAtPath(state, path, patch) {
|
|
1801
|
+
const [segment, ...rest] = path;
|
|
1802
|
+
if (segment === void 0) throw new CosystemError("Worker state patch path is invalid.");
|
|
1803
|
+
const container = clonePatchContainer(state, segment);
|
|
1804
|
+
if (rest.length === 0) {
|
|
1805
|
+
if (patch.op === "remove") {
|
|
1806
|
+
removePatchValue(container, segment);
|
|
1807
|
+
return container;
|
|
1808
|
+
}
|
|
1809
|
+
setPatchValue(container, segment, patch.value);
|
|
1810
|
+
return container;
|
|
1811
|
+
}
|
|
1812
|
+
setPatchValue(container, segment, applyPatchAtPath(getPatchValue(container, segment), rest, patch));
|
|
1813
|
+
return container;
|
|
1814
|
+
}
|
|
1815
|
+
function clonePatchContainer(state, nextSegment) {
|
|
1816
|
+
if (Array.isArray(state)) return [...state];
|
|
1817
|
+
if (isRecord(state)) return { ...state };
|
|
1818
|
+
return typeof nextSegment === "number" ? [] : {};
|
|
1819
|
+
}
|
|
1820
|
+
function getPatchValue(container, segment) {
|
|
1821
|
+
if (Array.isArray(container)) return container[toArrayIndex(segment)];
|
|
1822
|
+
return container[String(segment)];
|
|
1823
|
+
}
|
|
1824
|
+
function setPatchValue(container, segment, value) {
|
|
1825
|
+
if (Array.isArray(container)) {
|
|
1826
|
+
container[toArrayIndex(segment)] = value;
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
container[String(segment)] = value;
|
|
1830
|
+
}
|
|
1831
|
+
function removePatchValue(container, segment) {
|
|
1832
|
+
if (Array.isArray(container)) {
|
|
1833
|
+
container.splice(toArrayIndex(segment), 1);
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
delete container[String(segment)];
|
|
1837
|
+
}
|
|
1838
|
+
function toArrayIndex(segment) {
|
|
1839
|
+
if (typeof segment === "number") return segment;
|
|
1840
|
+
return Number(segment);
|
|
1841
|
+
}
|
|
1842
|
+
function normalizePatchPath(path) {
|
|
1843
|
+
if (Array.isArray(path)) return path.map((segment) => normalizePatchPathSegment(segment));
|
|
1844
|
+
if (typeof path === "string") {
|
|
1845
|
+
if (path === "" || path === "/") return [];
|
|
1846
|
+
return path.split("/").slice(1).map((segment) => normalizePatchPathSegment(segment.replaceAll("~1", "/").replaceAll("~0", "~")));
|
|
1847
|
+
}
|
|
1848
|
+
throw new CosystemError("Worker state patch path is invalid.");
|
|
1849
|
+
}
|
|
1850
|
+
function normalizePatchPathSegment(segment) {
|
|
1851
|
+
if (typeof segment === "number" || typeof segment === "string") return segment;
|
|
1852
|
+
throw new CosystemError("Worker state patch path segment is invalid.");
|
|
1853
|
+
}
|
|
1854
|
+
function isWorkerPatch(value) {
|
|
1855
|
+
if (!isRecord(value)) return false;
|
|
1856
|
+
return (value.op === "add" || value.op === "replace" || value.op === "remove") && (Array.isArray(value.path) || typeof value.path === "string");
|
|
1857
|
+
}
|
|
1023
1858
|
function createMemoryWorkerTransport(inbox, outbox) {
|
|
1024
1859
|
return {
|
|
1025
1860
|
post(message) {
|
|
@@ -1033,6 +1868,72 @@ function createMemoryWorkerTransport(inbox, outbox) {
|
|
|
1033
1868
|
}
|
|
1034
1869
|
};
|
|
1035
1870
|
}
|
|
1871
|
+
const workerMessageTypes = [
|
|
1872
|
+
"call",
|
|
1873
|
+
"result",
|
|
1874
|
+
"state",
|
|
1875
|
+
"ready"
|
|
1876
|
+
];
|
|
1877
|
+
const defaultBroadcastWorkerChannel = "cosystem:worker";
|
|
1878
|
+
const memoryBroadcastChannels = /* @__PURE__ */ new Map();
|
|
1879
|
+
let nextWorkerPeerId = 1;
|
|
1880
|
+
var MemoryBroadcastChannel = class {
|
|
1881
|
+
name;
|
|
1882
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
1883
|
+
#closed = false;
|
|
1884
|
+
constructor(name) {
|
|
1885
|
+
this.name = name;
|
|
1886
|
+
let channels = memoryBroadcastChannels.get(name);
|
|
1887
|
+
if (channels === void 0) {
|
|
1888
|
+
channels = /* @__PURE__ */ new Set();
|
|
1889
|
+
memoryBroadcastChannels.set(name, channels);
|
|
1890
|
+
}
|
|
1891
|
+
channels.add(this);
|
|
1892
|
+
}
|
|
1893
|
+
postMessage(message) {
|
|
1894
|
+
if (this.#closed) return;
|
|
1895
|
+
const channels = memoryBroadcastChannels.get(this.name);
|
|
1896
|
+
if (channels === void 0) return;
|
|
1897
|
+
for (const channel of channels) {
|
|
1898
|
+
if (channel === this || channel.#closed) continue;
|
|
1899
|
+
channel.dispatch({ data: message });
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
addEventListener(_type, listener) {
|
|
1903
|
+
if (!this.#closed) this.#listeners.add(listener);
|
|
1904
|
+
}
|
|
1905
|
+
removeEventListener(_type, listener) {
|
|
1906
|
+
this.#listeners.delete(listener);
|
|
1907
|
+
}
|
|
1908
|
+
close() {
|
|
1909
|
+
if (this.#closed) return;
|
|
1910
|
+
this.#closed = true;
|
|
1911
|
+
this.#listeners.clear();
|
|
1912
|
+
const channels = memoryBroadcastChannels.get(this.name);
|
|
1913
|
+
if (channels === void 0) return;
|
|
1914
|
+
channels.delete(this);
|
|
1915
|
+
if (channels.size === 0) memoryBroadcastChannels.delete(this.name);
|
|
1916
|
+
}
|
|
1917
|
+
dispatch(event) {
|
|
1918
|
+
for (const listener of this.#listeners) listener(event);
|
|
1919
|
+
}
|
|
1920
|
+
};
|
|
1921
|
+
function createWorkerPeerId() {
|
|
1922
|
+
const id = nextWorkerPeerId;
|
|
1923
|
+
nextWorkerPeerId += 1;
|
|
1924
|
+
return `peer:${id}`;
|
|
1925
|
+
}
|
|
1926
|
+
function isWorkerMessage(message) {
|
|
1927
|
+
if (typeof message !== "object" || message === null || !("type" in message)) return false;
|
|
1928
|
+
return workerMessageTypes.includes(message.type);
|
|
1929
|
+
}
|
|
1930
|
+
function isBroadcastWorkerMessageEnvelope(message) {
|
|
1931
|
+
if (!isRecord(message)) return false;
|
|
1932
|
+
return message.type === "cosystem:worker" && typeof message.channel === "string" && typeof message.source === "string" && (message.target === void 0 || typeof message.target === "string") && isWorkerMessage(message.message);
|
|
1933
|
+
}
|
|
1934
|
+
function isRecord(value) {
|
|
1935
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1936
|
+
}
|
|
1036
1937
|
function serializeError(error) {
|
|
1037
1938
|
if (error instanceof Error) return {
|
|
1038
1939
|
message: error.message,
|
|
@@ -1050,7 +1951,8 @@ function createRemoteError(error) {
|
|
|
1050
1951
|
if (error.stack !== void 0) remoteError.stack = error.stack;
|
|
1051
1952
|
return remoteError;
|
|
1052
1953
|
}
|
|
1954
|
+
function noop() {}
|
|
1053
1955
|
//#endregion
|
|
1054
|
-
export { AmbiguousProviderError, AsyncProviderInSyncResolutionError, CircularDependencyError, CosystemError, DuplicateProviderError, FrozenContainerError, InjectContextError, LifetimeLeakError, MissingProviderError, action, computed, createApp, createContainer, createMemoryWorkerTransportPair, createWorkerApp, createWorkerClient, defineModule, getModuleMetadata, inject, module, provide, state, testApp, token, tokenName };
|
|
1956
|
+
export { AmbiguousProviderError, AsyncProviderInSyncResolutionError, CircularDependencyError, CosystemError, DuplicateProviderError, FrozenContainerError, InjectContextError, LifetimeLeakError, MissingProviderError, action, computed, createApp, createBroadcastWorkerTransport, createContainer, createDataTransportWorkerTransport, createLoggerPlugin, createMemoryBroadcastChannel, createMemoryWorkerTransportPair, createPostMessageWorkerTransport, createWorkerApp, createWorkerClient, defineModule, effect, getModuleMetadata, inject, lazyModule, module, provide, runInAction, state, testApp, token, tokenName };
|
|
1055
1957
|
|
|
1056
1958
|
//# sourceMappingURL=index.mjs.map
|