@stratasync/core 0.2.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/README.md +83 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/model/base-model.d.ts +82 -0
- package/dist/model/base-model.d.ts.map +1 -0
- package/dist/model/base-model.js +219 -0
- package/dist/model/base-model.js.map +1 -0
- package/dist/model/cached-promise.d.ts +15 -0
- package/dist/model/cached-promise.d.ts.map +1 -0
- package/dist/model/cached-promise.js +50 -0
- package/dist/model/cached-promise.js.map +1 -0
- package/dist/model/collection.d.ts +33 -0
- package/dist/model/collection.d.ts.map +1 -0
- package/dist/model/collection.js +181 -0
- package/dist/model/collection.js.map +1 -0
- package/dist/model/hydration.d.ts +13 -0
- package/dist/model/hydration.d.ts.map +1 -0
- package/dist/model/hydration.js +2 -0
- package/dist/model/hydration.js.map +1 -0
- package/dist/model/observability.d.ts +23 -0
- package/dist/model/observability.d.ts.map +1 -0
- package/dist/model/observability.js +162 -0
- package/dist/model/observability.js.map +1 -0
- package/dist/reactivity/adapter.d.ts +145 -0
- package/dist/reactivity/adapter.d.ts.map +1 -0
- package/dist/reactivity/adapter.js +95 -0
- package/dist/reactivity/adapter.js.map +1 -0
- package/dist/schema/decorators.d.ts +8 -0
- package/dist/schema/decorators.d.ts.map +1 -0
- package/dist/schema/decorators.js +117 -0
- package/dist/schema/decorators.js.map +1 -0
- package/dist/schema/hash.d.ts +6 -0
- package/dist/schema/hash.d.ts.map +1 -0
- package/dist/schema/hash.js +76 -0
- package/dist/schema/hash.js.map +1 -0
- package/dist/schema/normalize.d.ts +5 -0
- package/dist/schema/normalize.d.ts.map +1 -0
- package/dist/schema/normalize.js +194 -0
- package/dist/schema/normalize.js.map +1 -0
- package/dist/schema/registry.d.ts +147 -0
- package/dist/schema/registry.d.ts.map +1 -0
- package/dist/schema/registry.js +304 -0
- package/dist/schema/registry.js.map +1 -0
- package/dist/schema/types.d.ts +215 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +2 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/store/types.d.ts +14 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +2 -0
- package/dist/store/types.js.map +1 -0
- package/dist/sync/delta-applier.d.ts +52 -0
- package/dist/sync/delta-applier.d.ts.map +1 -0
- package/dist/sync/delta-applier.js +110 -0
- package/dist/sync/delta-applier.js.map +1 -0
- package/dist/sync/rebase.d.ts +57 -0
- package/dist/sync/rebase.d.ts.map +1 -0
- package/dist/sync/rebase.js +155 -0
- package/dist/sync/rebase.js.map +1 -0
- package/dist/sync/sync-id.d.ts +17 -0
- package/dist/sync/sync-id.d.ts.map +1 -0
- package/dist/sync/sync-id.js +26 -0
- package/dist/sync/sync-id.js.map +1 -0
- package/dist/sync/types.d.ts +152 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/sync/types.js +2 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/transaction/archive.d.ts +16 -0
- package/dist/transaction/archive.d.ts.map +1 -0
- package/dist/transaction/archive.js +23 -0
- package/dist/transaction/archive.js.map +1 -0
- package/dist/transaction/create.d.ts +31 -0
- package/dist/transaction/create.d.ts.map +1 -0
- package/dist/transaction/create.js +121 -0
- package/dist/transaction/create.js.map +1 -0
- package/dist/transaction/types.d.ts +86 -0
- package/dist/transaction/types.d.ts.map +1 -0
- package/dist/transaction/types.js +2 -0
- package/dist/transaction/types.js.map +1 -0
- package/dist/utils/assign.d.ts +9 -0
- package/dist/utils/assign.d.ts.map +1 -0
- package/dist/utils/assign.js +20 -0
- package/dist/utils/assign.js.map +1 -0
- package/dist/utils/idempotency.d.ts +16 -0
- package/dist/utils/idempotency.d.ts.map +1 -0
- package/dist/utils/idempotency.js +39 -0
- package/dist/utils/idempotency.js.map +1 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @stratasync/core
|
|
2
|
+
|
|
3
|
+
Core model runtime, schema metadata, sync primitives, and transaction system for the stratasync stack.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
sync-core provides the foundation for the stratasync stack:
|
|
8
|
+
|
|
9
|
+
- **Model system** with decorator-based schema definitions
|
|
10
|
+
- **Delta application** with last-writer-wins conflict resolution (field-level)
|
|
11
|
+
- **Transaction creation and serialization** for client-server sync
|
|
12
|
+
- **Archive/unarchive** soft-delete support
|
|
13
|
+
- **Reactivity adapter** abstraction (implemented by sync-mobx)
|
|
14
|
+
- **Schema registry** with deterministic hashing for bootstrap validation
|
|
15
|
+
- **Identity map** and change tracking
|
|
16
|
+
|
|
17
|
+
Zero external dependencies — pure TypeScript.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @stratasync/core
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Model Definitions
|
|
26
|
+
|
|
27
|
+
Models use TypeScript decorators for schema metadata:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { ClientModel, Property, ManyToOne, OneToMany } from "@stratasync/core";
|
|
31
|
+
|
|
32
|
+
@ClientModel("Task", {
|
|
33
|
+
loadStrategy: "instant",
|
|
34
|
+
tableName: "task",
|
|
35
|
+
groupKey: "teamId",
|
|
36
|
+
})
|
|
37
|
+
class Task {
|
|
38
|
+
@Property({ type: "string" })
|
|
39
|
+
title: string;
|
|
40
|
+
|
|
41
|
+
@Property({ type: "number" })
|
|
42
|
+
createdAt: number;
|
|
43
|
+
|
|
44
|
+
@ManyToOne("Team", { fk: "teamId" })
|
|
45
|
+
team: LazyReference<Team>;
|
|
46
|
+
|
|
47
|
+
@OneToMany("TaskChecklistItem", { fk: "taskId" })
|
|
48
|
+
checklistItems: LazyCollection<TaskChecklistItem>;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Schema System
|
|
53
|
+
|
|
54
|
+
The schema registry normalizes decorator metadata into a queryable format:
|
|
55
|
+
|
|
56
|
+
- `@ClientModel` — registers the model class with load strategy and table mapping
|
|
57
|
+
- `@Property` — defines a field with its type (`string`, `number`, `boolean`, `json`)
|
|
58
|
+
- `@ManyToOne` / `@Reference` — defines a foreign key relation with lazy loading
|
|
59
|
+
- `@OneToMany` / `@ReferenceCollection` — defines a reverse relation as a lazy collection
|
|
60
|
+
- `@BackReference` — back-reference without storage
|
|
61
|
+
- `@ReferenceArray` — many-to-many via through model
|
|
62
|
+
- `@EphemeralProperty` — non-persisted property
|
|
63
|
+
|
|
64
|
+
## Sync Primitives
|
|
65
|
+
|
|
66
|
+
- **Delta applier** — applies server deltas to local model instances using last-writer-wins
|
|
67
|
+
- **Rebase logic** — reconciles local optimistic changes with server-confirmed state
|
|
68
|
+
- **Transaction helpers** — `createInsertTransaction`, `createUpdateTransaction`, `createDeleteTransaction`, `createArchiveTransaction`, `createUnarchiveTransaction`, `createUndoTransaction`
|
|
69
|
+
- **Serialization** — compact format with abbreviated field names for storage and transport
|
|
70
|
+
- **Schema hash** — deterministic, order-independent hash for detecting schema changes
|
|
71
|
+
|
|
72
|
+
## Package Structure
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
src/
|
|
76
|
+
├── model/ — Model base class, hydration, cached promises, collections
|
|
77
|
+
├── schema/ — Decorators, type definitions, normalization, registry, hashing
|
|
78
|
+
├── transaction/ — Creation helpers, serialization, archive utilities, types
|
|
79
|
+
├── sync/ — Delta applier, rebase logic, sync IDs, sync types
|
|
80
|
+
├── store/ — SyncStore interface (implemented by sync-client)
|
|
81
|
+
├── reactivity/ — ReactivityAdapter interface (implemented by sync-mobx)
|
|
82
|
+
└── utils/ — ID generation, assignment helpers
|
|
83
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { Model } from "./model/base-model.js";
|
|
2
|
+
export { makeObservableProperty, makeReferenceModelProperty, setBoxFactory, } from "./model/observability.js";
|
|
3
|
+
export type { DisposeFn, ObservableArray, ObservableBox, ObservableMap, ObservableOptions, ReactionOptions, ReactivityAdapter, } from "./reactivity/adapter.js";
|
|
4
|
+
export { noopReactivityAdapter } from "./reactivity/adapter.js";
|
|
5
|
+
export { BackReference, ClientModel, OneToMany, Property, Reference, ReferenceArray, } from "./schema/decorators.js";
|
|
6
|
+
export { computeSchemaHash } from "./schema/hash.js";
|
|
7
|
+
export { ModelRegistry } from "./schema/registry.js";
|
|
8
|
+
export type { ModelMetadata, ModelRegistrySnapshot, SchemaDefinition, TransactionAction, } from "./schema/types.js";
|
|
9
|
+
export type { SyncStore } from "./store/types.js";
|
|
10
|
+
export { applyDeltas } from "./sync/delta-applier.js";
|
|
11
|
+
export type { RebaseConflict, RebaseOptions } from "./sync/rebase.js";
|
|
12
|
+
export { rebaseTransactions } from "./sync/rebase.js";
|
|
13
|
+
export type { SyncId } from "./sync/sync-id.js";
|
|
14
|
+
export { compareSyncId, isSyncIdGreaterThan, maxSyncId, ZERO_SYNC_ID, } from "./sync/sync-id.js";
|
|
15
|
+
export type { BatchLoadOptions, BatchRequest, BootstrapMetadata, BootstrapOptions, ConnectionState, DeltaPacket, DeltaSubscription, ModelRow, SubscribeOptions, SyncAction, SyncActionType, SyncClientState, } from "./sync/types.js";
|
|
16
|
+
export type { ArchiveTransactionOptions, UnarchiveTransactionOptions, } from "./transaction/archive.js";
|
|
17
|
+
export { captureArchiveState, createArchivePayload, createUnarchivePatch, createUnarchivePayload, readArchivedAt, } from "./transaction/archive.js";
|
|
18
|
+
export { createArchiveTransaction, createDeleteTransaction, createInsertTransaction, createTransactionBatch, createUnarchiveTransaction, createUndoTransaction, createUpdateTransaction, } from "./transaction/create.js";
|
|
19
|
+
export type { MutateResult, Transaction, TransactionBatch, TransactionResult, TransactionState, } from "./transaction/types.js";
|
|
20
|
+
export { generateUUID, getOrCreateClientId } from "./utils/idempotency.js";
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,aAAa,GACd,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,SAAS,EACT,eAAe,EACf,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EACL,aAAa,EACb,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,cAAc,GACf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,YAAY,GACb,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// biome-ignore-all lint/performance/noBarrelFile: This is the package's main entry point
|
|
2
|
+
export { Model } from "./model/base-model.js";
|
|
3
|
+
export { makeObservableProperty, makeReferenceModelProperty, setBoxFactory, } from "./model/observability.js";
|
|
4
|
+
export { noopReactivityAdapter } from "./reactivity/adapter.js";
|
|
5
|
+
export { BackReference, ClientModel, OneToMany, Property, Reference, ReferenceArray, } from "./schema/decorators.js";
|
|
6
|
+
export { computeSchemaHash } from "./schema/hash.js";
|
|
7
|
+
export { ModelRegistry } from "./schema/registry.js";
|
|
8
|
+
export { applyDeltas } from "./sync/delta-applier.js";
|
|
9
|
+
export { rebaseTransactions } from "./sync/rebase.js";
|
|
10
|
+
export { compareSyncId, isSyncIdGreaterThan, maxSyncId, ZERO_SYNC_ID, } from "./sync/sync-id.js";
|
|
11
|
+
export { captureArchiveState, createArchivePayload, createUnarchivePatch, createUnarchivePayload, readArchivedAt, } from "./transaction/archive.js";
|
|
12
|
+
export { createArchiveTransaction, createDeleteTransaction, createInsertTransaction, createTransactionBatch, createUnarchiveTransaction, createUndoTransaction, createUpdateTransaction, } from "./transaction/create.js";
|
|
13
|
+
export { generateUUID, getOrCreateClientId } from "./utils/idempotency.js";
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yFAAyF;AAEzF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,aAAa,GACd,MAAM,0BAA0B,CAAC;AAUlC,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EACL,aAAa,EACb,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,cAAc,GACf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAQrD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,YAAY,GACb,MAAM,mBAAmB,CAAC;AAmB3B,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,cAAc,GACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AAQjC,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { SyncStore } from "../store/types.js";
|
|
2
|
+
import type { Hydrated } from "./hydration.js";
|
|
3
|
+
export interface ChangeSnapshot {
|
|
4
|
+
changes: Record<string, unknown>;
|
|
5
|
+
original: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Base model class for Done models.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Model {
|
|
11
|
+
private readonly modifiedProperties;
|
|
12
|
+
private suppressTracking;
|
|
13
|
+
private __cachedModelName?;
|
|
14
|
+
/** Primary key (uuid) */
|
|
15
|
+
id: string;
|
|
16
|
+
/** Whether lazy references have been hydrated */
|
|
17
|
+
hydrated: boolean;
|
|
18
|
+
/** MobX boxes for observable properties */
|
|
19
|
+
_mobx: Record<string, {
|
|
20
|
+
get(): unknown;
|
|
21
|
+
set(value: unknown): void;
|
|
22
|
+
}>;
|
|
23
|
+
/** Backing data store */
|
|
24
|
+
__data: Record<string, unknown>;
|
|
25
|
+
/** Store reference */
|
|
26
|
+
store?: SyncStore;
|
|
27
|
+
/**
|
|
28
|
+
* Returns the registered model name (cached after first access).
|
|
29
|
+
*/
|
|
30
|
+
get __modelName(): string;
|
|
31
|
+
/**
|
|
32
|
+
* Makes this model observable (placeholder for MobX integration).
|
|
33
|
+
* Subclasses override this to wire up MobX reactivity.
|
|
34
|
+
*/
|
|
35
|
+
makeObservable(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Hydrates lazy references and collections for this model.
|
|
38
|
+
*/
|
|
39
|
+
hydrate(): Promise<Hydrated<this>>;
|
|
40
|
+
/**
|
|
41
|
+
* Tracks property changes for transaction creation.
|
|
42
|
+
*/
|
|
43
|
+
propertyChanged(propertyName: string, oldValue: unknown, newValue: unknown): void;
|
|
44
|
+
/**
|
|
45
|
+
* Records the original value for a changed property.
|
|
46
|
+
*/
|
|
47
|
+
markPropertyChanged(propertyName: string, oldValue: unknown, _newValue: unknown): void;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a snapshot of changes for UpdateTransaction creation.
|
|
50
|
+
*/
|
|
51
|
+
changeSnapshot(): ChangeSnapshot;
|
|
52
|
+
/**
|
|
53
|
+
* Clears tracked property changes.
|
|
54
|
+
*/
|
|
55
|
+
clearChanges(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Applies updates without recording change tracking.
|
|
58
|
+
*/
|
|
59
|
+
_applyUpdate(changes: Record<string, unknown>): void;
|
|
60
|
+
/**
|
|
61
|
+
* Serializes model data for persistence.
|
|
62
|
+
*/
|
|
63
|
+
toJSON(): Record<string, unknown>;
|
|
64
|
+
/**
|
|
65
|
+
* Saves pending changes by creating an update transaction.
|
|
66
|
+
*/
|
|
67
|
+
save(): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Deletes this model.
|
|
70
|
+
*/
|
|
71
|
+
delete(): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Archives this model.
|
|
74
|
+
*/
|
|
75
|
+
archive(): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Unarchives this model.
|
|
78
|
+
*/
|
|
79
|
+
unarchive(): Promise<void>;
|
|
80
|
+
private withSuppressedTracking;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=base-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-model.d.ts","sourceRoot":"","sources":["../../src/model/base-model.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAMnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,qBAAa,KAAK;IAEhB,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA8B;IACjE,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,iBAAiB,CAAC,CAAS;IAEnC,yBAAyB;IACzB,EAAE,SAAM;IACR,iDAAiD;IACjD,QAAQ,UAAS;IACjB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,IAAI,OAAO,CAAC;QAAC,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC,CAAM;IAC1E,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;IACrC,sBAAsB;IACtB,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAUxB;IAED;;;OAGG;IAEH,cAAc,IAAI,IAAI;IAItB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAyCxC;;OAEG;IACH,eAAe,CACb,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,OAAO,GAChB,IAAI;IAOP;;OAEG;IACH,mBAAmB,CACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,OAAO,EACjB,SAAS,EAAE,OAAO,GACjB,IAAI;IAeP;;OAEG;IACH,cAAc,IAAI,cAAc;IAYhC;;OAEG;IACH,YAAY,IAAI,IAAI;IAIpB;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAepD;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQjC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B3B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAS7B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAU9B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAShC,OAAO,CAAC,sBAAsB;CAQ/B"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { ModelRegistry } from "../schema/registry.js";
|
|
2
|
+
import { captureArchiveState, createArchivePayload, } from "../transaction/archive.js";
|
|
3
|
+
import { CachedPromise } from "./cached-promise.js";
|
|
4
|
+
/**
|
|
5
|
+
* Base model class for Done models.
|
|
6
|
+
*/
|
|
7
|
+
export class Model {
|
|
8
|
+
// ── Tracking infrastructure (must initialize before any decorated field) ──
|
|
9
|
+
modifiedProperties = new Map();
|
|
10
|
+
suppressTracking = 0;
|
|
11
|
+
__cachedModelName;
|
|
12
|
+
/** Primary key (uuid) */
|
|
13
|
+
id = "";
|
|
14
|
+
/** Whether lazy references have been hydrated */
|
|
15
|
+
hydrated = false;
|
|
16
|
+
/** MobX boxes for observable properties */
|
|
17
|
+
_mobx = {};
|
|
18
|
+
/** Backing data store */
|
|
19
|
+
__data = {};
|
|
20
|
+
/** Store reference */
|
|
21
|
+
store;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the registered model name (cached after first access).
|
|
24
|
+
*/
|
|
25
|
+
get __modelName() {
|
|
26
|
+
if (this.__cachedModelName !== undefined) {
|
|
27
|
+
return this.__cachedModelName;
|
|
28
|
+
}
|
|
29
|
+
const name = ModelRegistry.getModelName(this.constructor);
|
|
30
|
+
this.__cachedModelName =
|
|
31
|
+
name ?? this.constructor.name ?? "Model";
|
|
32
|
+
return this.__cachedModelName;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Makes this model observable (placeholder for MobX integration).
|
|
36
|
+
* Subclasses override this to wire up MobX reactivity.
|
|
37
|
+
*/
|
|
38
|
+
// oxlint-disable-next-line eslint(class-methods-use-this) -- override hook for subclasses
|
|
39
|
+
makeObservable() {
|
|
40
|
+
// Observability is handled per-property via decorators.
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Hydrates lazy references and collections for this model.
|
|
44
|
+
*/
|
|
45
|
+
async hydrate() {
|
|
46
|
+
if (this.hydrated) {
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
const referenced = ModelRegistry.getReferencedProperties(this.__modelName);
|
|
50
|
+
const pending = [];
|
|
51
|
+
for (const [propertyName, meta] of referenced.entries()) {
|
|
52
|
+
const value = this[propertyName];
|
|
53
|
+
if (!value) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (meta.type === "referenceModel") {
|
|
57
|
+
if (value instanceof CachedPromise) {
|
|
58
|
+
pending.push(value.getPromise());
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (value instanceof Promise) {
|
|
62
|
+
pending.push(value);
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (meta.type === "referenceCollection") {
|
|
67
|
+
const hydrator = value;
|
|
68
|
+
if (typeof hydrator.hydrate === "function") {
|
|
69
|
+
pending.push(hydrator.hydrate());
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (pending.length > 0) {
|
|
74
|
+
await Promise.all(pending);
|
|
75
|
+
}
|
|
76
|
+
this.hydrated = true;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Tracks property changes for transaction creation.
|
|
81
|
+
*/
|
|
82
|
+
propertyChanged(propertyName, oldValue, newValue) {
|
|
83
|
+
if (!this.modifiedProperties || this.suppressTracking > 0) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.markPropertyChanged(propertyName, oldValue, newValue);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Records the original value for a changed property.
|
|
90
|
+
*/
|
|
91
|
+
markPropertyChanged(propertyName, oldValue, _newValue) {
|
|
92
|
+
if (this.modifiedProperties.has(propertyName)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const meta = ModelRegistry.getModelProperties(this.__modelName).get(propertyName);
|
|
96
|
+
const serializer = meta?.serializer;
|
|
97
|
+
const serializedOldValue = serializer
|
|
98
|
+
? serializer.serialize(oldValue)
|
|
99
|
+
: oldValue;
|
|
100
|
+
this.modifiedProperties.set(propertyName, serializedOldValue);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Creates a snapshot of changes for UpdateTransaction creation.
|
|
104
|
+
*/
|
|
105
|
+
changeSnapshot() {
|
|
106
|
+
const changes = {};
|
|
107
|
+
const original = {};
|
|
108
|
+
for (const [propertyName, oldValue] of this.modifiedProperties.entries()) {
|
|
109
|
+
original[propertyName] = oldValue;
|
|
110
|
+
changes[propertyName] = this.__data[propertyName];
|
|
111
|
+
}
|
|
112
|
+
return { changes, original };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Clears tracked property changes.
|
|
116
|
+
*/
|
|
117
|
+
clearChanges() {
|
|
118
|
+
this.modifiedProperties.clear();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Applies updates without recording change tracking.
|
|
122
|
+
*/
|
|
123
|
+
_applyUpdate(changes) {
|
|
124
|
+
this.withSuppressedTracking(() => {
|
|
125
|
+
for (const [key, value] of Object.entries(changes)) {
|
|
126
|
+
if (key === "id" && typeof value === "string") {
|
|
127
|
+
this.id = value;
|
|
128
|
+
}
|
|
129
|
+
const current = this.__data[key];
|
|
130
|
+
if (!Object.is(current, value)) {
|
|
131
|
+
this[key] = value;
|
|
132
|
+
this.__data[key] = value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Serializes model data for persistence.
|
|
139
|
+
*/
|
|
140
|
+
toJSON() {
|
|
141
|
+
const data = { ...this.__data };
|
|
142
|
+
if (this.id) {
|
|
143
|
+
data.id = this.id;
|
|
144
|
+
}
|
|
145
|
+
return data;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Saves pending changes by creating an update transaction.
|
|
149
|
+
*/
|
|
150
|
+
async save() {
|
|
151
|
+
if (!this.store) {
|
|
152
|
+
throw new Error("Model store is not configured");
|
|
153
|
+
}
|
|
154
|
+
if (!this.id) {
|
|
155
|
+
if (!this.store.create) {
|
|
156
|
+
throw new Error("Model store does not support create");
|
|
157
|
+
}
|
|
158
|
+
const created = await this.store.create(this.__modelName, this.toJSON());
|
|
159
|
+
this._applyUpdate(created);
|
|
160
|
+
this.clearChanges();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const snapshot = this.changeSnapshot();
|
|
164
|
+
if (Object.keys(snapshot.changes).length === 0) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (!this.store.update) {
|
|
168
|
+
throw new Error("Model store does not support update");
|
|
169
|
+
}
|
|
170
|
+
await this.store.update(this.__modelName, this.id, snapshot.changes, {
|
|
171
|
+
original: snapshot.original,
|
|
172
|
+
});
|
|
173
|
+
this.clearChanges();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Deletes this model.
|
|
177
|
+
*/
|
|
178
|
+
async delete() {
|
|
179
|
+
if (!this.store?.delete) {
|
|
180
|
+
throw new Error("Model store does not support delete");
|
|
181
|
+
}
|
|
182
|
+
await this.store.delete(this.__modelName, this.id, {
|
|
183
|
+
original: this.toJSON(),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Archives this model.
|
|
188
|
+
*/
|
|
189
|
+
async archive() {
|
|
190
|
+
if (!this.store?.archive) {
|
|
191
|
+
throw new Error("Model store does not support archive");
|
|
192
|
+
}
|
|
193
|
+
await this.store.archive(this.__modelName, this.id, {
|
|
194
|
+
archivedAt: createArchivePayload().archivedAt ?? undefined,
|
|
195
|
+
original: captureArchiveState(this.__data),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Unarchives this model.
|
|
200
|
+
*/
|
|
201
|
+
async unarchive() {
|
|
202
|
+
if (!this.store?.unarchive) {
|
|
203
|
+
throw new Error("Model store does not support unarchive");
|
|
204
|
+
}
|
|
205
|
+
await this.store.unarchive(this.__modelName, this.id, {
|
|
206
|
+
original: captureArchiveState(this.__data),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
withSuppressedTracking(fn) {
|
|
210
|
+
this.suppressTracking += 1;
|
|
211
|
+
try {
|
|
212
|
+
fn();
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
this.suppressTracking -= 1;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=base-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-model.js","sourceRoot":"","sources":["../../src/model/base-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type CachedPromiseStatus = "pending" | "fulfilled" | "rejected";
|
|
2
|
+
export declare class CachedPromise<T> implements PromiseLike<T> {
|
|
3
|
+
private readonly promise;
|
|
4
|
+
status: CachedPromiseStatus;
|
|
5
|
+
value: T | undefined;
|
|
6
|
+
error: unknown;
|
|
7
|
+
constructor(promise: Promise<T>);
|
|
8
|
+
static resolve<T = undefined>(value?: T | PromiseLike<T>): CachedPromise<Awaited<T>>;
|
|
9
|
+
getPromise(): Promise<T>;
|
|
10
|
+
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null | undefined): Promise<TResult1 | TResult2>;
|
|
11
|
+
catch<TResult = never>(onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null | undefined): Promise<T | TResult>;
|
|
12
|
+
finally(onfinally?: (() => void) | null | undefined): Promise<T>;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=cached-promise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cached-promise.d.ts","sourceRoot":"","sources":["../../src/model/cached-promise.ts"],"names":[],"mappings":"AAAA,KAAK,mBAAmB,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAKhE,qBAAa,aAAa,CAAC,CAAC,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,MAAM,EAAE,mBAAmB,CAAa;IACxC,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;gBAEH,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAe/B,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,EAC1B,KAAK,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GACzB,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAe5B,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC;IAMxB,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,KAAK,EACjC,WAAW,CAAC,EACR,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GAChD,IAAI,GACJ,SAAS,EACb,UAAU,CAAC,EACP,CAAC,CAAC,MAAM,EAAE,OAAO,KAAK,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,GACvD,IAAI,GACJ,SAAS,GACZ,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAI/B,KAAK,CAAC,OAAO,GAAG,KAAK,EACnB,UAAU,CAAC,EACP,CAAC,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,GACrD,IAAI,GACJ,SAAS,GACZ,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC;IAIvB,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC;CAGjE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const isThenable = (value) => typeof value?.then === "function";
|
|
2
|
+
export class CachedPromise {
|
|
3
|
+
promise;
|
|
4
|
+
status = "pending";
|
|
5
|
+
value;
|
|
6
|
+
error;
|
|
7
|
+
constructor(promise) {
|
|
8
|
+
this.promise = (async () => {
|
|
9
|
+
try {
|
|
10
|
+
const value = await promise;
|
|
11
|
+
this.status = "fulfilled";
|
|
12
|
+
this.value = value;
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
this.status = "rejected";
|
|
17
|
+
this.error = error;
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
})();
|
|
21
|
+
}
|
|
22
|
+
static resolve(value) {
|
|
23
|
+
if (value instanceof CachedPromise) {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
const cached = new CachedPromise(
|
|
27
|
+
// oxlint-disable-next-line prefer-await-to-then -- fire-and-forget pattern
|
|
28
|
+
Promise.resolve(value));
|
|
29
|
+
if (!isThenable(value)) {
|
|
30
|
+
cached.status = "fulfilled";
|
|
31
|
+
cached.value = value;
|
|
32
|
+
}
|
|
33
|
+
return cached;
|
|
34
|
+
}
|
|
35
|
+
getPromise() {
|
|
36
|
+
return this.promise;
|
|
37
|
+
}
|
|
38
|
+
// biome-ignore lint/suspicious/noThenProperty: CachedPromise is intentionally thenable.
|
|
39
|
+
// oxlint-disable-next-line no-thenable
|
|
40
|
+
then(onfulfilled, onrejected) {
|
|
41
|
+
return this.promise.then(onfulfilled, onrejected);
|
|
42
|
+
}
|
|
43
|
+
catch(onrejected) {
|
|
44
|
+
return this.promise.catch(onrejected);
|
|
45
|
+
}
|
|
46
|
+
finally(onfinally) {
|
|
47
|
+
return this.promise.finally(onfinally);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=cached-promise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cached-promise.js","sourceRoot":"","sources":["../../src/model/cached-promise.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAG,CAAC,KAAc,EAAiC,EAAE,CACnE,OAAQ,KAA4B,EAAE,IAAI,KAAK,UAAU,CAAC;AAE5D,MAAM,OAAO,aAAa;IACP,OAAO,CAAa;IACrC,MAAM,GAAwB,SAAS,CAAC;IACxC,KAAK,CAAgB;IACrB,KAAK,CAAU;IAEf,YAAY,OAAmB;QAC7B,IAAI,CAAC,OAAO,GAAG,CAAC,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC;gBAC5B,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC;gBAC1B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;gBACnB,OAAO,KAAK,CAAC;YACf,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;gBACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;gBACnB,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IAED,MAAM,CAAC,OAAO,CACZ,KAA0B;QAE1B,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;YACnC,OAAO,KAAkC,CAAC;QAC5C,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,aAAa;QAC9B,2EAA2E;QAC3E,OAAO,CAAC,OAAO,CAAC,KAA2B,CAAC,CAC7C,CAAC;QACF,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC;YAC5B,MAAM,CAAC,KAAK,GAAG,KAAmB,CAAC;QACrC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,wFAAwF;IACxF,uCAAuC;IACvC,IAAI,CACF,WAGa,EACb,UAGa;QAEb,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CACH,UAGa;QAEb,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,CAAC,SAA2C;QACjD,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;CACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Model } from "./base-model.js";
|
|
2
|
+
interface CollectionContext {
|
|
3
|
+
modelName?: string;
|
|
4
|
+
foreignKey?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class LazyCollection<T extends Model> {
|
|
7
|
+
private elementsCache;
|
|
8
|
+
private hydrated;
|
|
9
|
+
private hydrating?;
|
|
10
|
+
private owner?;
|
|
11
|
+
private propertyName?;
|
|
12
|
+
private modelName?;
|
|
13
|
+
private foreignKey?;
|
|
14
|
+
private attached;
|
|
15
|
+
constructor(items?: Iterable<T>);
|
|
16
|
+
attach(owner: Model, propertyName: string, context?: CollectionContext): void;
|
|
17
|
+
private ensureHydrating;
|
|
18
|
+
get elements(): T[];
|
|
19
|
+
get length(): number;
|
|
20
|
+
map<U>(callback: (value: T, index: number, array: T[]) => U, thisArg?: unknown): U[];
|
|
21
|
+
filter(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: unknown): T[];
|
|
22
|
+
find(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: unknown): T | undefined;
|
|
23
|
+
every(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: unknown): boolean;
|
|
24
|
+
add(item: T): void;
|
|
25
|
+
remove(item: T): boolean;
|
|
26
|
+
clear(): void;
|
|
27
|
+
toArray(): T[];
|
|
28
|
+
[Symbol.iterator](): IterableIterator<T>;
|
|
29
|
+
hydrate(): Promise<T[]>;
|
|
30
|
+
private loadElements;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/model/collection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAiB7C,UAAU,iBAAiB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAyCD,qBAAa,cAAc,CAAC,CAAC,SAAS,KAAK;IACzC,OAAO,CAAC,aAAa,CAAW;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAC,CAAqB;IACvC,OAAO,CAAC,KAAK,CAAC,CAAQ;IACtB,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;gBAEb,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IAO/B,MAAM,CACJ,KAAK,EAAE,KAAK,EACZ,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,iBAAsB,GAC9B,IAAI;IA0BP,OAAO,CAAC,eAAe;IAUvB,IAAI,QAAQ,IAAI,CAAC,EAAE,CAGlB;IAED,IAAI,MAAM,IAAI,MAAM,CAGnB;IAED,GAAG,CAAC,CAAC,EACH,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EACpD,OAAO,CAAC,EAAE,OAAO,GAChB,CAAC,EAAE;IAMN,MAAM,CACJ,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,OAAO,EAC1D,OAAO,CAAC,EAAE,OAAO,GAChB,CAAC,EAAE;IAMN,IAAI,CACF,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,OAAO,EAC1D,OAAO,CAAC,EAAE,OAAO,GAEhB,CAAC,GAAG,SAAS;IAMhB,KAAK,CACH,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,OAAO,EAE1D,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO;IAMV,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAKlB,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO;IAUxB,KAAK,IAAI,IAAI;IAMb,OAAO,IAAI,CAAC,EAAE;IAKd,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAKxC,OAAO,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;YA4BT,YAAY;CAsC3B"}
|