@ng-org/orm 0.1.2-alpha.1 → 0.1.2-alpha.10
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 +151 -230
- package/dist/connector/applyPatches.d.ts +19 -49
- package/dist/connector/applyPatches.d.ts.map +1 -1
- package/dist/connector/applyPatches.js +57 -58
- package/dist/connector/discrete/discreteOrmSubscriptionHandler.d.ts +168 -0
- package/dist/connector/discrete/discreteOrmSubscriptionHandler.d.ts.map +1 -0
- package/dist/connector/discrete/discreteOrmSubscriptionHandler.js +309 -0
- package/dist/connector/getObjects.d.ts +11 -0
- package/dist/connector/getObjects.d.ts.map +1 -0
- package/dist/connector/getObjects.js +26 -0
- package/dist/connector/initNg.d.ts +33 -2
- package/dist/connector/initNg.d.ts.map +1 -1
- package/dist/connector/initNg.js +30 -0
- package/dist/connector/insertObject.d.ts +9 -0
- package/dist/connector/insertObject.d.ts.map +1 -0
- package/dist/connector/insertObject.js +24 -0
- package/dist/connector/ormSubscriptionHandler.d.ts +167 -0
- package/dist/connector/ormSubscriptionHandler.d.ts.map +1 -0
- package/dist/connector/ormSubscriptionHandler.js +400 -0
- package/dist/connector/utils.d.ts +17 -0
- package/dist/connector/utils.d.ts.map +1 -0
- package/dist/connector/utils.js +68 -0
- package/dist/frontendAdapters/react/index.d.ts +2 -1
- package/dist/frontendAdapters/react/index.d.ts.map +1 -1
- package/dist/frontendAdapters/react/index.js +2 -1
- package/dist/frontendAdapters/react/useDiscrete.d.ts +104 -0
- package/dist/frontendAdapters/react/useDiscrete.d.ts.map +1 -0
- package/dist/frontendAdapters/react/useDiscrete.js +151 -0
- package/dist/frontendAdapters/react/useShape.d.ts +69 -2
- package/dist/frontendAdapters/react/useShape.d.ts.map +1 -1
- package/dist/frontendAdapters/react/useShape.js +89 -8
- package/dist/frontendAdapters/svelte/index.d.ts +2 -1
- package/dist/frontendAdapters/svelte/index.d.ts.map +1 -1
- package/dist/frontendAdapters/svelte/index.js +2 -1
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts +94 -0
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts.map +1 -0
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.js +137 -0
- package/dist/frontendAdapters/svelte/useShape.svelte.d.ts +79 -10
- package/dist/frontendAdapters/svelte/useShape.svelte.d.ts.map +1 -1
- package/dist/frontendAdapters/svelte/useShape.svelte.js +88 -9
- package/dist/frontendAdapters/svelte4/index.d.ts +5 -0
- package/dist/frontendAdapters/svelte4/index.d.ts.map +1 -0
- package/dist/{connector/createSignalObjectForShape.js → frontendAdapters/svelte4/index.js} +3 -15
- package/dist/frontendAdapters/svelte4/useDiscrete.svelte.d.ts +84 -0
- package/dist/frontendAdapters/svelte4/useDiscrete.svelte.d.ts.map +1 -0
- package/dist/frontendAdapters/svelte4/useDiscrete.svelte.js +126 -0
- package/dist/frontendAdapters/svelte4/useShape.svelte.d.ts +77 -0
- package/dist/frontendAdapters/svelte4/useShape.svelte.d.ts.map +1 -0
- package/dist/frontendAdapters/svelte4/useShape.svelte.js +91 -0
- package/dist/frontendAdapters/utils.d.ts +2 -0
- package/dist/frontendAdapters/utils.d.ts.map +1 -0
- package/dist/frontendAdapters/utils.js +14 -0
- package/dist/frontendAdapters/vue/index.d.ts +2 -1
- package/dist/frontendAdapters/vue/index.d.ts.map +1 -1
- package/dist/frontendAdapters/vue/index.js +2 -1
- package/dist/frontendAdapters/vue/useDiscrete.d.ts +99 -0
- package/dist/frontendAdapters/vue/useDiscrete.d.ts.map +1 -0
- package/dist/frontendAdapters/vue/useDiscrete.js +123 -0
- package/dist/frontendAdapters/vue/useShape.d.ts +73 -2
- package/dist/frontendAdapters/vue/useShape.d.ts.map +1 -1
- package/dist/frontendAdapters/vue/useShape.js +79 -4
- package/dist/index.d.ts +13 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -6
- package/dist/types.d.ts +63 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +13 -1
- package/package.json +37 -48
- package/dist/connector/applyPatches.test.d.ts +0 -2
- package/dist/connector/applyPatches.test.d.ts.map +0 -1
- package/dist/connector/applyPatches.test.js +0 -772
- package/dist/connector/createSignalObjectForShape.d.ts +0 -14
- package/dist/connector/createSignalObjectForShape.d.ts.map +0 -1
- package/dist/connector/ormConnectionHandler.d.ts +0 -47
- package/dist/connector/ormConnectionHandler.d.ts.map +0 -1
- package/dist/connector/ormConnectionHandler.js +0 -240
- package/src/connector/applyPatches.test.ts +0 -842
- package/src/connector/applyPatches.ts +0 -404
- package/src/connector/createSignalObjectForShape.ts +0 -35
- package/src/connector/initNg.ts +0 -30
- package/src/connector/ormConnectionHandler.ts +0 -300
- package/src/frontendAdapters/react/index.ts +0 -2
- package/src/frontendAdapters/react/useShape.ts +0 -36
- package/src/frontendAdapters/svelte/index.ts +0 -2
- package/src/frontendAdapters/svelte/useShape.svelte.ts +0 -46
- package/src/frontendAdapters/vue/index.ts +0 -2
- package/src/frontendAdapters/vue/useShape.ts +0 -33
- package/src/index.ts +0 -14
- package/src/types.ts +0 -26
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
// Licensed under the Apache License, Version 2.0
|
|
4
|
+
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
|
5
|
+
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
|
6
|
+
// at your option. All files in the project carrying such
|
|
7
|
+
// notice may not be copied, modified, or distributed except
|
|
8
|
+
// according to those terms.
|
|
9
|
+
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
10
|
+
import { applyPatchesToDeepSignal } from "../applyPatches.js";
|
|
11
|
+
import { ngSession } from "../initNg.js";
|
|
12
|
+
import { deepSignal, watch as watchDeepSignal, } from "@ng-org/alien-deepsignals";
|
|
13
|
+
/**
|
|
14
|
+
* Delay in ms to wait before closing connection.\
|
|
15
|
+
* Useful when a hook unsubscribes and resubscribes in a short time interval
|
|
16
|
+
* so that no new connections need to be set up.
|
|
17
|
+
*/
|
|
18
|
+
const WAIT_BEFORE_CLOSE = 500;
|
|
19
|
+
/**
|
|
20
|
+
* Class for managing RDF-based ORM subscriptions with the engine.
|
|
21
|
+
*
|
|
22
|
+
* You have two options on how to interact with the ORM:
|
|
23
|
+
* - Use a hook for your favorite framework under `@ng-org/orm/react|vue|svelte`
|
|
24
|
+
* - Call {@link OrmSubscription.getOrCreate} to create a subscription manually
|
|
25
|
+
*
|
|
26
|
+
* For more information about RDF-based ORM subscriptions,
|
|
27
|
+
* see the [README](../../../README.md) and follow the tutorial.
|
|
28
|
+
*/
|
|
29
|
+
export class DiscreteOrmSubscription {
|
|
30
|
+
/** Global store of all subscriptions. We use that for pooling. */
|
|
31
|
+
static idToEntry = new Map();
|
|
32
|
+
/** The document ID (NURI) of the subscribed document. */
|
|
33
|
+
documentId;
|
|
34
|
+
_signalObject;
|
|
35
|
+
stopSignalListening;
|
|
36
|
+
/** The subscription ID kept as an identifier for communicating with the verifier. */
|
|
37
|
+
subscriptionId;
|
|
38
|
+
/** The number of OrmSubscriptions with the same shape and scope (for pooling). */
|
|
39
|
+
refCount;
|
|
40
|
+
/** When true, modifications from the signalObject are not processed. */
|
|
41
|
+
suspendDeepWatcher;
|
|
42
|
+
/** @readonly True, if a transaction is running. */
|
|
43
|
+
inTransaction = false;
|
|
44
|
+
/** Aggregation of patches to be sent when in transaction. */
|
|
45
|
+
pendingPatches;
|
|
46
|
+
/** **Await to ensure that the subscription is established and the data arrived.** */
|
|
47
|
+
readyPromise;
|
|
48
|
+
closeOrmSubscription;
|
|
49
|
+
/** Function to call once initial data has been applied. */
|
|
50
|
+
resolveReady;
|
|
51
|
+
constructor(documentId) {
|
|
52
|
+
// @ts-expect-error
|
|
53
|
+
window.ormDiscreteSignalConnections = DiscreteOrmSubscription.idToEntry;
|
|
54
|
+
// @ts-expect-error
|
|
55
|
+
window.OrmDiscreteConnection = DiscreteOrmSubscription;
|
|
56
|
+
// @ts-expect-error
|
|
57
|
+
window.OrmDiscreteIncomingPatches = [];
|
|
58
|
+
this.documentId = documentId;
|
|
59
|
+
this.refCount = 1;
|
|
60
|
+
this.closeOrmSubscription = () => { };
|
|
61
|
+
this.suspendDeepWatcher = false;
|
|
62
|
+
// Initialize per-entry readiness promise that resolves in setUpConnection
|
|
63
|
+
this.readyPromise = new Promise((resolve) => {
|
|
64
|
+
this.resolveReady = resolve;
|
|
65
|
+
});
|
|
66
|
+
ngSession.then(async ({ ng, session }) => {
|
|
67
|
+
try {
|
|
68
|
+
this.closeOrmSubscription = await ng.orm_start_discrete(documentId, session.session_id, this.onBackendMessage);
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
console.error(e);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The signalObject containing all data of the document
|
|
77
|
+
* (once subscription is established).
|
|
78
|
+
* The object behaves like a regular object or array with a couple of additions:
|
|
79
|
+
* - Modifications are immediately propagated back to the database.
|
|
80
|
+
* - Database changes are immediately reflected in the object.
|
|
81
|
+
* - Watch for object changes using {@link watchDeepSignal}.
|
|
82
|
+
* - Objects in arrays receive a unique `@id` property.
|
|
83
|
+
*/
|
|
84
|
+
get signalObject() {
|
|
85
|
+
return this._signalObject;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Returns an OrmSubscription which subscribes to the given
|
|
89
|
+
* document in a 2-way binding.
|
|
90
|
+
*
|
|
91
|
+
* You **find the document data** in the **`signalObject`**,
|
|
92
|
+
* once {@link readyPromise} resolves.
|
|
93
|
+
* This is a {@link DeepSignal} object or array, depending on
|
|
94
|
+
* your CRDT document (e.g. YArray vs YMap). The signalObject
|
|
95
|
+
* behaves like a regular set to the outside but has a couple
|
|
96
|
+
* of additional features:
|
|
97
|
+
* - Modifications are propagated back to the document.
|
|
98
|
+
* Note that multiple immediate modifications in the same task,
|
|
99
|
+
* e.g. `obj[0] = "foo"; obj[1] = "bar"` are batched together
|
|
100
|
+
* and sent in a subsequent microtask.
|
|
101
|
+
* - External document changes are immediately reflected in the object.
|
|
102
|
+
* - Watch for object changes using {@link watchDeepSignal}.
|
|
103
|
+
*
|
|
104
|
+
* You can use **transactions**, to prevent excessive calls to the engine
|
|
105
|
+
* with {@link beginTransaction} and {@link commitTransaction}.
|
|
106
|
+
*
|
|
107
|
+
* In many cases, you are advised to use a hook for your
|
|
108
|
+
* favorite framework under `@ng-org/orm/react|vue|svelte`
|
|
109
|
+
* instead of calling `getOrCreate` directly.
|
|
110
|
+
*
|
|
111
|
+
* Call `{@link close}, to close the subscription.
|
|
112
|
+
*
|
|
113
|
+
* Note: If another call to `getOrCreate` was previously made
|
|
114
|
+
* and {@link close} was not called on it (or only shortly after),
|
|
115
|
+
* it will return the same OrmSubscription (pooling).
|
|
116
|
+
*
|
|
117
|
+
* @param documentId The document ID (NURI) of the CRDT
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* // We assume you have created a CRDT document already, as below.
|
|
122
|
+
* // const documentId = await ng.doc_create(
|
|
123
|
+
* // session_id,
|
|
124
|
+
* // crdt, // "Automerge" | "YMap" | "YArray". YArray is for root arrays, the other two have objects at root.
|
|
125
|
+
* // crdt === "Automerge" ? "data:json" : crdt === "YMap ? "data:map" : "data:array",
|
|
126
|
+
* // "store",
|
|
127
|
+
* // undefined
|
|
128
|
+
* // );
|
|
129
|
+
* const subscription = DiscreteOrmSubscription.getOrCreate(documentId);
|
|
130
|
+
* // Wait for data.
|
|
131
|
+
* await subscription.readyPromise;
|
|
132
|
+
*
|
|
133
|
+
* const document = subscription.signalObject;
|
|
134
|
+
* if (!document.expenses) {
|
|
135
|
+
* document.expenses = [];
|
|
136
|
+
* }
|
|
137
|
+
* document.expenses.push({
|
|
138
|
+
* name: "New Expense name",
|
|
139
|
+
* description: "Expense description"
|
|
140
|
+
* });
|
|
141
|
+
*
|
|
142
|
+
* // Await promise to run the below code in a new task.
|
|
143
|
+
* // That will have push the changes to the database.
|
|
144
|
+
* await Promise.resolve();
|
|
145
|
+
*
|
|
146
|
+
* // Here, the expense modifications have been committed
|
|
147
|
+
* // (unless you had previously called subscription.beginTransaction()).
|
|
148
|
+
* // The data is available in subscriptions running on a different device too.
|
|
149
|
+
*
|
|
150
|
+
* subscription.close();
|
|
151
|
+
*
|
|
152
|
+
* // If you create a new subscription with the same document within a couple of 100ms,
|
|
153
|
+
* // The subscription hasn't been closed and the old one is returned so that the data
|
|
154
|
+
* // is available instantly. This is especially useful in the context of unmounting and remounting frontend frameworks.
|
|
155
|
+
* const subscription2 = DiscreteOrmSubscription.getOrCreate(documentId);
|
|
156
|
+
*
|
|
157
|
+
* subscription2.signalObject.expenses.push({
|
|
158
|
+
* name: "Second expense",
|
|
159
|
+
* description: "Second description"
|
|
160
|
+
* });
|
|
161
|
+
*
|
|
162
|
+
* subscription2.close();
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
static getOrCreate = (documentId) => {
|
|
166
|
+
// If we already have a connection open,
|
|
167
|
+
// return that signal object and just increase the reference count.
|
|
168
|
+
// Otherwise, open a new one.
|
|
169
|
+
const existingConnection = DiscreteOrmSubscription.idToEntry.get(documentId);
|
|
170
|
+
if (existingConnection) {
|
|
171
|
+
existingConnection.refCount += 1;
|
|
172
|
+
return existingConnection;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
const newConnection = new DiscreteOrmSubscription(documentId);
|
|
176
|
+
DiscreteOrmSubscription.idToEntry.set(documentId, newConnection);
|
|
177
|
+
return newConnection;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Stop the subscription.
|
|
182
|
+
*
|
|
183
|
+
* **If there is more than one subscription with the document ID,
|
|
184
|
+
* the orm subscription won't close yet.**
|
|
185
|
+
*
|
|
186
|
+
* Additionally, the closing of the subscription is delayed by a couple hundred milliseconds
|
|
187
|
+
* so that when frontend frameworks unmount and soon mount a component again with the same
|
|
188
|
+
* document ID, we reuse the same orm subscription.
|
|
189
|
+
*/
|
|
190
|
+
close = () => {
|
|
191
|
+
setTimeout(() => {
|
|
192
|
+
if (this.refCount > 0)
|
|
193
|
+
this.refCount--;
|
|
194
|
+
if (this.refCount === 0) {
|
|
195
|
+
DiscreteOrmSubscription.idToEntry.delete(this.documentId);
|
|
196
|
+
this.closeOrmSubscription();
|
|
197
|
+
}
|
|
198
|
+
}, WAIT_BEFORE_CLOSE);
|
|
199
|
+
};
|
|
200
|
+
/** Handle updates (patches) coming from signal object modifications. */
|
|
201
|
+
onSignalObjectUpdate = async ({ patches, }) => {
|
|
202
|
+
if (this.suspendDeepWatcher || !patches.length)
|
|
203
|
+
return;
|
|
204
|
+
const ormPatches = deepPatchesToWasm(patches);
|
|
205
|
+
// If in transaction, collect patches immediately (no await before).
|
|
206
|
+
if (this.inTransaction) {
|
|
207
|
+
this.pendingPatches?.push(...ormPatches);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Wait for session and subscription to be initialized.
|
|
211
|
+
const { ng, session } = await ngSession;
|
|
212
|
+
await this.readyPromise;
|
|
213
|
+
ng.discrete_orm_update(this.subscriptionId, ormPatches, session.session_id);
|
|
214
|
+
};
|
|
215
|
+
/** Handle messages coming from the engine (initial data or patches). */
|
|
216
|
+
onBackendMessage = (message) => {
|
|
217
|
+
const data = message?.V0;
|
|
218
|
+
if (data?.DiscreteOrmInitial) {
|
|
219
|
+
this.handleInitialResponse(data.DiscreteOrmInitial);
|
|
220
|
+
}
|
|
221
|
+
else if (data?.DiscreteOrmUpdate) {
|
|
222
|
+
this.onBackendUpdate(data.DiscreteOrmUpdate);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
console.warn("Received unknown ORM message from backend", message);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
handleInitialResponse = ([initialData, subscriptionId]) => {
|
|
229
|
+
this.subscriptionId = subscriptionId;
|
|
230
|
+
const signalObject = deepSignal(initialData, {
|
|
231
|
+
syntheticIdPropertyName: undefined,
|
|
232
|
+
});
|
|
233
|
+
this._signalObject = signalObject;
|
|
234
|
+
const { stopListening } = watchDeepSignal(this._signalObject, this.onSignalObjectUpdate);
|
|
235
|
+
this.stopSignalListening = stopListening;
|
|
236
|
+
// Resolve readiness after initial data is committed and watcher armed.
|
|
237
|
+
this.resolveReady();
|
|
238
|
+
};
|
|
239
|
+
/** Handle incoming patches from the engine */
|
|
240
|
+
onBackendUpdate = (patches) => {
|
|
241
|
+
// @ts-expect-error
|
|
242
|
+
window.OrmDiscreteIncomingPatches.push(patches);
|
|
243
|
+
this.suspendDeepWatcher = true;
|
|
244
|
+
applyPatchesToDeepSignal(this._signalObject, patches, "discrete");
|
|
245
|
+
// Use queueMicrotask to ensure watcher is re-enabled _after_ batch completes
|
|
246
|
+
queueMicrotask(() => {
|
|
247
|
+
this.suspendDeepWatcher = false;
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Begins a transaction that batches changes to be committed to the database.
|
|
252
|
+
* This is useful for performance reasons.
|
|
253
|
+
*
|
|
254
|
+
* Note that this does not disable reactivity of the `signalObject`.
|
|
255
|
+
* Modifications keep being rendered instantly.
|
|
256
|
+
*/
|
|
257
|
+
beginTransaction = () => {
|
|
258
|
+
this.inTransaction = true;
|
|
259
|
+
this.pendingPatches = [];
|
|
260
|
+
this.readyPromise.then(() => {
|
|
261
|
+
// Use a listener that immediately triggers on object modifications.
|
|
262
|
+
// We don't need the deep-signal's batching (through microtasks) here.
|
|
263
|
+
this.stopSignalListening?.();
|
|
264
|
+
const { stopListening } = watchDeepSignal(this.signalObject, this.onSignalObjectUpdate, { triggerInstantly: true });
|
|
265
|
+
this.stopSignalListening = stopListening;
|
|
266
|
+
});
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* Commits a transactions sending all modifications made during the transaction
|
|
270
|
+
* (started with `beginTransaction`) to the database.
|
|
271
|
+
* @throws if no transaction is open.
|
|
272
|
+
*/
|
|
273
|
+
commitTransaction = async () => {
|
|
274
|
+
if (!this.inTransaction) {
|
|
275
|
+
throw new Error("No transaction is open. Call `beginTransaction` first.");
|
|
276
|
+
}
|
|
277
|
+
const { ng, session } = await ngSession;
|
|
278
|
+
await this.readyPromise;
|
|
279
|
+
this.inTransaction = false;
|
|
280
|
+
if (this.pendingPatches?.length == 0) {
|
|
281
|
+
// Nothing to send to the backend.
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
ng.discrete_orm_update(this.subscriptionId, this.pendingPatches, session.session_id);
|
|
285
|
+
}
|
|
286
|
+
this.pendingPatches = undefined;
|
|
287
|
+
// Go back to the regular object modification listening where we want batching
|
|
288
|
+
// scheduled in a microtask only triggered after the main task.
|
|
289
|
+
// This way we prevent excessive calls to the backend.
|
|
290
|
+
this.stopSignalListening();
|
|
291
|
+
const { stopListening } = watchDeepSignal(this.signalObject, this.onSignalObjectUpdate);
|
|
292
|
+
this.stopSignalListening = stopListening;
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Converts DeepSignal patches to ORM Wasm-compatible patches
|
|
297
|
+
* @param patches DeepSignal patches
|
|
298
|
+
* @returns Patches with stringified path
|
|
299
|
+
*
|
|
300
|
+
* @ignore
|
|
301
|
+
*/
|
|
302
|
+
export function deepPatchesToWasm(patches) {
|
|
303
|
+
return patches.flatMap((patch) => {
|
|
304
|
+
if (patch.op === "add" && patch.type === "set" && !patch.value?.length)
|
|
305
|
+
return [];
|
|
306
|
+
const path = "/" + patch.path.join("/");
|
|
307
|
+
return { ...patch, path };
|
|
308
|
+
});
|
|
309
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseType, ShapeType } from "@ng-org/shex-orm";
|
|
2
|
+
import { Scope } from "../types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Utility for retrieving objects once without establishing a two-way subscription.
|
|
5
|
+
*
|
|
6
|
+
* @param shapeType The shape type of the objects to be retrieved.
|
|
7
|
+
* @param scope The scope of the objects to be retrieved.
|
|
8
|
+
* @returns A set of all objects matching the shape and scope
|
|
9
|
+
*/
|
|
10
|
+
export declare function getObjects<T extends BaseType>(shapeType: ShapeType<T>, scope?: Scope): Promise<import("@ng-org/alien-deepsignals").DeepSignalSet<T>>;
|
|
11
|
+
//# sourceMappingURL=getObjects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getObjects.d.ts","sourceRoot":"","sources":["../../src/connector/getObjects.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,QAAQ,EAC/C,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,KAAK,GAAE,KAAU,iEAUpB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
// Licensed under the Apache License, Version 2.0
|
|
4
|
+
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
|
5
|
+
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
|
6
|
+
// at your option. All files in the project carrying such
|
|
7
|
+
// notice may not be copied, modified, or distributed except
|
|
8
|
+
// according to those terms.
|
|
9
|
+
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
10
|
+
import { OrmSubscription } from "./ormSubscriptionHandler.js";
|
|
11
|
+
import { deepClone } from "./utils.js";
|
|
12
|
+
/**
|
|
13
|
+
* Utility for retrieving objects once without establishing a two-way subscription.
|
|
14
|
+
*
|
|
15
|
+
* @param shapeType The shape type of the objects to be retrieved.
|
|
16
|
+
* @param scope The scope of the objects to be retrieved.
|
|
17
|
+
* @returns A set of all objects matching the shape and scope
|
|
18
|
+
*/
|
|
19
|
+
export async function getObjects(shapeType, scope = {}) {
|
|
20
|
+
const connection = OrmSubscription.getOrCreate(shapeType, scope);
|
|
21
|
+
await connection.readyPromise;
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
connection.close();
|
|
24
|
+
}, 1_000);
|
|
25
|
+
return deepClone(connection.signalObject);
|
|
26
|
+
}
|
|
@@ -1,14 +1,45 @@
|
|
|
1
1
|
import * as NG from "@ng-org/lib-wasm";
|
|
2
|
-
type Session = {
|
|
2
|
+
export type Session = {
|
|
3
3
|
session_id: string | number;
|
|
4
4
|
protected_store_id: string;
|
|
5
5
|
private_store_id: string;
|
|
6
6
|
public_store_id: string;
|
|
7
|
+
ng: typeof NG;
|
|
8
|
+
[key: string]: unknown;
|
|
7
9
|
};
|
|
10
|
+
/** Resolves to the NG session and the ng implementation. */
|
|
8
11
|
export declare const ngSession: Promise<{
|
|
9
12
|
ng: typeof NG;
|
|
10
13
|
session: Session;
|
|
11
14
|
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the ORM by passing the ng implementation and session.
|
|
17
|
+
*
|
|
18
|
+
* **This is the first thing you need to to before using the ORM.**
|
|
19
|
+
*
|
|
20
|
+
* @param ngImpl The NextGraph API, e.g. exported from `@ng-org/web`.
|
|
21
|
+
* @param session The established NextGraph session.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { ng, init } from "@ng-org/web";
|
|
26
|
+
* import { initNg as initNgSignals, Session } from "@ng-org/orm";
|
|
27
|
+
* let session: Session;
|
|
28
|
+
*
|
|
29
|
+
* // Call as early as possible as it will redirect to the auth page.
|
|
30
|
+
* await init(
|
|
31
|
+
* async (event: any) => {
|
|
32
|
+
* session = event.session;
|
|
33
|
+
* session!.ng ??= ng;
|
|
34
|
+
*
|
|
35
|
+
* // Call initNgSignals
|
|
36
|
+
* initNgSignals(ng, session);
|
|
37
|
+
* },
|
|
38
|
+
* true,
|
|
39
|
+
* []
|
|
40
|
+
* );
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
12
44
|
export declare function initNgSignals(ngImpl: typeof NG, session: Session): void;
|
|
13
|
-
export {};
|
|
14
45
|
//# sourceMappingURL=initNg.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initNg.d.ts","sourceRoot":"","sources":["../../src/connector/initNg.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEvC,
|
|
1
|
+
{"version":3,"file":"initNg.d.ts","sourceRoot":"","sources":["../../src/connector/initNg.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEvC,MAAM,MAAM,OAAO,GAAG;IAClB,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,EAAE,EAAE,OAAO,EAAE,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B,CAAC;AAIF,4DAA4D;AAC5D,eAAO,MAAM,SAAS;QAAqB,OAAO,EAAE;aAAW,OAAO;EAIrE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,QAEhE"}
|
package/dist/connector/initNg.js
CHANGED
|
@@ -8,9 +8,39 @@
|
|
|
8
8
|
// according to those terms.
|
|
9
9
|
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
10
10
|
let resolveNgSession;
|
|
11
|
+
/** Resolves to the NG session and the ng implementation. */
|
|
11
12
|
export const ngSession = new Promise((resolve) => {
|
|
12
13
|
resolveNgSession = resolve;
|
|
13
14
|
});
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the ORM by passing the ng implementation and session.
|
|
17
|
+
*
|
|
18
|
+
* **This is the first thing you need to to before using the ORM.**
|
|
19
|
+
*
|
|
20
|
+
* @param ngImpl The NextGraph API, e.g. exported from `@ng-org/web`.
|
|
21
|
+
* @param session The established NextGraph session.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { ng, init } from "@ng-org/web";
|
|
26
|
+
* import { initNg as initNgSignals, Session } from "@ng-org/orm";
|
|
27
|
+
* let session: Session;
|
|
28
|
+
*
|
|
29
|
+
* // Call as early as possible as it will redirect to the auth page.
|
|
30
|
+
* await init(
|
|
31
|
+
* async (event: any) => {
|
|
32
|
+
* session = event.session;
|
|
33
|
+
* session!.ng ??= ng;
|
|
34
|
+
*
|
|
35
|
+
* // Call initNgSignals
|
|
36
|
+
* initNgSignals(ng, session);
|
|
37
|
+
* },
|
|
38
|
+
* true,
|
|
39
|
+
* []
|
|
40
|
+
* );
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
14
44
|
export function initNgSignals(ngImpl, session) {
|
|
15
45
|
resolveNgSession({ ng: ngImpl, session });
|
|
16
46
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BaseType, ShapeType } from "@ng-org/shex-orm";
|
|
2
|
+
/**
|
|
3
|
+
* Utility for adding ORM-typed objects to the database without the need for subscribing to documents.
|
|
4
|
+
*
|
|
5
|
+
* @param shapeType The shape type of the objects to be inserted.
|
|
6
|
+
* @param object The object to be inserted.
|
|
7
|
+
*/
|
|
8
|
+
export declare function insertObject<T extends BaseType>(shapeType: ShapeType<T>, object: T): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=insertObject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insertObject.d.ts","sourceRoot":"","sources":["../../src/connector/insertObject.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGvD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,QAAQ,EACjD,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,CAAC,iBASZ"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
|
|
2
|
+
// All rights reserved.
|
|
3
|
+
// Licensed under the Apache License, Version 2.0
|
|
4
|
+
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
|
5
|
+
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
|
6
|
+
// at your option. All files in the project carrying such
|
|
7
|
+
// notice may not be copied, modified, or distributed except
|
|
8
|
+
// according to those terms.
|
|
9
|
+
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
10
|
+
import { OrmSubscription } from "./ormSubscriptionHandler.js";
|
|
11
|
+
/**
|
|
12
|
+
* Utility for adding ORM-typed objects to the database without the need for subscribing to documents.
|
|
13
|
+
*
|
|
14
|
+
* @param shapeType The shape type of the objects to be inserted.
|
|
15
|
+
* @param object The object to be inserted.
|
|
16
|
+
*/
|
|
17
|
+
export async function insertObject(shapeType, object) {
|
|
18
|
+
const connection = OrmSubscription.getOrCreate(shapeType, {
|
|
19
|
+
graphs: [], // Subscribe to no documents
|
|
20
|
+
});
|
|
21
|
+
await connection.readyPromise;
|
|
22
|
+
connection.signalObject.add(object);
|
|
23
|
+
connection.close();
|
|
24
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { type Scope } from "../types.ts";
|
|
2
|
+
import { Patch } from "./applyPatches.ts";
|
|
3
|
+
import type { DeepSignalSet } from "@ng-org/alien-deepsignals";
|
|
4
|
+
import type { ShapeType, BaseType } from "@ng-org/shex-orm";
|
|
5
|
+
/**
|
|
6
|
+
* Class for managing RDF-based ORM subscriptions with the engine.
|
|
7
|
+
*
|
|
8
|
+
* You have two options on how to interact with the ORM:
|
|
9
|
+
* - Use a hook for your favorite framework under `@ng-org/orm/react|vue|svelte`
|
|
10
|
+
* - Call {@link OrmSubscription.getOrCreate} to create a subscription manually
|
|
11
|
+
*
|
|
12
|
+
* For more information about RDF-based ORM subscriptions,
|
|
13
|
+
* see the README and follow the tutorial.
|
|
14
|
+
*/
|
|
15
|
+
export declare class OrmSubscription<T extends BaseType> {
|
|
16
|
+
/** Global store of all subscriptions. We use that for pooling. */
|
|
17
|
+
private static idToEntry;
|
|
18
|
+
/** The shape type that is subscribed to. */
|
|
19
|
+
readonly shapeType: ShapeType<T>;
|
|
20
|
+
/** The {@link Scope} of the subscription. */
|
|
21
|
+
readonly scope: Scope;
|
|
22
|
+
/**
|
|
23
|
+
* The signalObject containing all data matching the shape and scope
|
|
24
|
+
* (once subscription is established).
|
|
25
|
+
* The object is of type {@link DeepSignalSet} which
|
|
26
|
+
* to the outside behaves like a regular set but has a couple of
|
|
27
|
+
* additional features:
|
|
28
|
+
* - Modifications are immediately propagated back to the database.
|
|
29
|
+
* - Database changes are immediately reflected in the object.
|
|
30
|
+
* - `.getBy(graphIri, subjectIri)` utility for quicker access to objects in set.
|
|
31
|
+
* - `.first()` utility to get the first element added to the set.
|
|
32
|
+
* - the iterator utilities, e.g. `.map()`, `.filter()`, ...
|
|
33
|
+
* - Watch for object changes using {@link watchDeepSignal}.
|
|
34
|
+
*/
|
|
35
|
+
readonly signalObject: DeepSignalSet<T>;
|
|
36
|
+
private stopSignalListening;
|
|
37
|
+
/** The subscription ID kept as an identifier for communicating with the verifier. */
|
|
38
|
+
private subscriptionId;
|
|
39
|
+
/** The number of OrmSubscriptions with the same shape and scope (for pooling). */
|
|
40
|
+
private refCount;
|
|
41
|
+
/** Identifier as a combination of shape type and scope. Prevents duplications. */
|
|
42
|
+
private identifier;
|
|
43
|
+
/** When true, modifications from the signalObject are not processed. */
|
|
44
|
+
suspendDeepWatcher: boolean;
|
|
45
|
+
/** True, if a transaction is running. */
|
|
46
|
+
inTransaction: boolean;
|
|
47
|
+
/** Aggregation of patches to be sent when in transaction. */
|
|
48
|
+
pendingPatches: Patch[] | undefined;
|
|
49
|
+
/** **Await to ensure that the subscription is established and the data arrived.** */
|
|
50
|
+
readyPromise: Promise<void>;
|
|
51
|
+
private closeOrmSubscription;
|
|
52
|
+
/** Function to call once initial data has been applied. */
|
|
53
|
+
private resolveReady;
|
|
54
|
+
private static cleanupSignalRegistry;
|
|
55
|
+
private constructor();
|
|
56
|
+
/**
|
|
57
|
+
* Returns an OrmSubscription which subscribes to the given
|
|
58
|
+
* {@link ShapeType} and {@link Scope} in a 2-way binding.
|
|
59
|
+
*
|
|
60
|
+
* You **find the data** and objects matching the shape and scope
|
|
61
|
+
* in the **`signalObject`** once {@link readyPromise} resolves. This is a {@link DeepSignalSet} which
|
|
62
|
+
* to the outside behaves like a regular set but has a couple of
|
|
63
|
+
* additional features:
|
|
64
|
+
* - Modifications are propagated back to the database.
|
|
65
|
+
* Note that multiple immediate modifications in the same task,
|
|
66
|
+
* e.g. `obj[0] = "foo"; obj[1] = "bar"` are batched together
|
|
67
|
+
* and sent in a subsequent microtask.
|
|
68
|
+
* - Database changes are immediately reflected in the object.
|
|
69
|
+
* - `.getBy(graphIri, subjectIri)` utility for quicker access to objects in set.
|
|
70
|
+
* - `.first()` utility to get the first element added to the set.
|
|
71
|
+
* - the iterator utilities, e.g. `.map()`, `.filter()`, ...
|
|
72
|
+
* - Watch for object changes using {@link watchDeepSignal}.
|
|
73
|
+
*
|
|
74
|
+
* You can use **transactions**, to prevent excessive calls to the database
|
|
75
|
+
* with {@link beginTransaction} and {@link commitTransaction}.
|
|
76
|
+
*
|
|
77
|
+
* In many cases, you are advised to use a hook for your
|
|
78
|
+
* favorite framework under `@ng-org/orm/react|vue|svelte`
|
|
79
|
+
* instead of calling `getOrCreate` directly.
|
|
80
|
+
*
|
|
81
|
+
* Call {@link close}, to close the subscription.
|
|
82
|
+
*
|
|
83
|
+
* Note: If another call to `getOrCreate` was previously made
|
|
84
|
+
* and `close` was not called on it (or only shortly after),
|
|
85
|
+
* it will return the same OrmSubscription.
|
|
86
|
+
*
|
|
87
|
+
* @param shapeType The {@link ShapeType}
|
|
88
|
+
* @param scope The {@link Scope}. If no scope is given, the whole store is considered.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* // We assume you have created a graph document already, as below.
|
|
93
|
+
* // const documentId = await ng.doc_create(
|
|
94
|
+
* // session_id,
|
|
95
|
+
* // "Graph",
|
|
96
|
+
* // "data:graph",
|
|
97
|
+
* // "store",
|
|
98
|
+
* // undefined
|
|
99
|
+
* // );
|
|
100
|
+
* const subscription = OrmSubscription.getOrCreate(ExpenseShapeType, {graphs: [graphIri]});
|
|
101
|
+
* // Wait for data.
|
|
102
|
+
* await subscription.readyPromise;
|
|
103
|
+
*
|
|
104
|
+
* const expense = subscription.signalObject.first()
|
|
105
|
+
* expense.name = "updated name";
|
|
106
|
+
* expense.description = "updated description";
|
|
107
|
+
*
|
|
108
|
+
* // Await promise to run the below code in a new task.
|
|
109
|
+
* // That will push the changes to the database.
|
|
110
|
+
* await Promise.resolve();
|
|
111
|
+
*
|
|
112
|
+
* // Here, the expense modifications have been have been committed
|
|
113
|
+
* // (unless you had previously called subscription.beginTransaction()).
|
|
114
|
+
* // The data is available in subscriptions running on a different device too.
|
|
115
|
+
*
|
|
116
|
+
* subscription.close();
|
|
117
|
+
* // If you create a new subscription with the same document within a couple of 100ms,
|
|
118
|
+
* // The subscription hasn't been closed and the old one is returned so that the data
|
|
119
|
+
* // is available instantly. This is especially useful in the context of frontend frameworks.
|
|
120
|
+
* const subscription2 = OrmSubscription.getOrCreate(ExpenseShapeType, {graphs: [graphIri]});
|
|
121
|
+
*
|
|
122
|
+
* subscription2.signalObject.add({
|
|
123
|
+
* "@graph": graphIri,
|
|
124
|
+
* "@id": "", // Leave empty to auto-assign one.
|
|
125
|
+
* name": "A new expense",
|
|
126
|
+
* description: "A new description"
|
|
127
|
+
* });
|
|
128
|
+
*
|
|
129
|
+
* subscription2.close()
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
static getOrCreate: <T_1 extends BaseType>(shapeType: ShapeType<T_1>, scope: Scope) => OrmSubscription<T_1>;
|
|
133
|
+
/**
|
|
134
|
+
* Stop the subscription.
|
|
135
|
+
*
|
|
136
|
+
* **If there is more than one subscription with the same shape type and scope
|
|
137
|
+
* the orm subscription will persist.**
|
|
138
|
+
*
|
|
139
|
+
* Additionally, the closing of the subscription is delayed by a couple hundred milliseconds
|
|
140
|
+
* so that when frontend frameworks unmount and soon mount a component again with the same
|
|
141
|
+
* shape type and scope, we reuse the same orm subscription.
|
|
142
|
+
*/
|
|
143
|
+
close: () => void;
|
|
144
|
+
/** Handle updates (patches) coming from signal object modifications. */
|
|
145
|
+
private onSignalObjectUpdate;
|
|
146
|
+
/** Handle messages coming from the engine (initial data or patches). */
|
|
147
|
+
private onBackendMessage;
|
|
148
|
+
private handleInitialResponse;
|
|
149
|
+
/** Handle incoming patches from the engine */
|
|
150
|
+
private onBackendUpdate;
|
|
151
|
+
/** Function to create random subject NURIs for newly created nested objects. */
|
|
152
|
+
private signalObjectPropGenerator;
|
|
153
|
+
/**
|
|
154
|
+
* Begins a transaction that batches changes to be committed to the database.
|
|
155
|
+
* This is useful for performance reasons.
|
|
156
|
+
*
|
|
157
|
+
* Note that this does not disable reactivity of the `signalObject`.
|
|
158
|
+
* Modifications keep being rendered.
|
|
159
|
+
*/
|
|
160
|
+
beginTransaction: () => void;
|
|
161
|
+
/**
|
|
162
|
+
* Commits a transactions sending all modifications made during the transaction
|
|
163
|
+
* (started with `beginTransaction`) to the database.
|
|
164
|
+
*/
|
|
165
|
+
commitTransaction: () => Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=ormSubscriptionHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ormSubscriptionHandler.d.ts","sourceRoot":"","sources":["../../src/connector/ormSubscriptionHandler.ts"],"names":[],"mappings":"AAUA,OAAO,EAAkB,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAA4B,KAAK,EAAE,MAAM,mBAAmB,CAAC;AASpE,OAAO,KAAK,EAER,aAAa,EAEhB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAU5D;;;;;;;;;GASG;AACH,qBAAa,eAAe,CAAC,CAAC,SAAS,QAAQ;IAC3C,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,SAAS,CAA2C;IAEnE,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACjC,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACxC,OAAO,CAAC,mBAAmB,CAAa;IACxC,qFAAqF;IACrF,OAAO,CAAC,cAAc,CAAqB;IAC3C,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAS;IACzB,kFAAkF;IAClF,OAAO,CAAC,UAAU,CAAS;IAC3B,wEAAwE;IACxE,kBAAkB,EAAE,OAAO,CAAC;IAC5B,yCAAyC;IACzC,aAAa,EAAE,OAAO,CAAS;IAC/B,6DAA6D;IAC7D,cAAc,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;IACpC,qFAAqF;IACrF,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,CAAC,oBAAoB,CAAa;IACzC,2DAA2D;IAC3D,OAAO,CAAC,YAAY,CAAc;IAGlC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAUrB;IAEf,OAAO;IAqDP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2EG;IACH,OAAc,WAAW,GAAI,GAAC,SAAS,QAAQ,EAC3C,WAAW,SAAS,CAAC,GAAC,CAAC,EACvB,OAAO,KAAK,KACb,eAAe,CAAC,GAAC,CAAC,CAkBnB;IAEF;;;;;;;;;OASG;IACI,KAAK,aAYV;IAEF,wEAAwE;IACxE,OAAO,CAAC,oBAAoB,CAoB1B;IAEF,wEAAwE;IACxE,OAAO,CAAC,gBAAgB,CAStB;IAEF,OAAO,CAAC,qBAAqB,CAuB3B;IAEF,8CAA8C;IAC9C,OAAO,CAAC,eAAe,CAOrB;IAEF,gFAAgF;IAChF,OAAO,CAAC,yBAAyB,CAgD/B;IAEF;;;;;;OAMG;IACI,gBAAgB,aAarB;IAEF;;;OAGG;IACI,iBAAiB,sBAkCtB;CACL"}
|