@ng-org/orm 0.1.2-alpha.8 → 0.1.2-alpha.9
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 +74 -92
- package/dist/connector/applyPatches.d.ts +13 -4
- package/dist/connector/applyPatches.d.ts.map +1 -1
- package/dist/connector/applyPatches.js +9 -5
- package/dist/connector/discrete/discreteOrmSubscriptionHandler.d.ts +156 -0
- package/dist/connector/discrete/discreteOrmSubscriptionHandler.d.ts.map +1 -0
- package/dist/connector/discrete/{discreteOrmConnectionHandler.js → discreteOrmSubscriptionHandler.js} +130 -19
- package/dist/connector/getObjects.js +2 -2
- package/dist/connector/initNg.d.ts +35 -0
- package/dist/connector/initNg.d.ts.map +1 -1
- package/dist/connector/initNg.js +35 -0
- package/dist/connector/insertObject.js +2 -2
- package/dist/connector/ormSubscriptionHandler.d.ts +166 -0
- package/dist/connector/ormSubscriptionHandler.d.ts.map +1 -0
- package/dist/connector/{ormConnectionHandler.js → ormSubscriptionHandler.js} +157 -32
- package/dist/frontendAdapters/react/useDiscrete.d.ts +89 -69
- package/dist/frontendAdapters/react/useDiscrete.d.ts.map +1 -1
- package/dist/frontendAdapters/react/useDiscrete.js +103 -81
- package/dist/frontendAdapters/react/useShape.d.ts +55 -55
- package/dist/frontendAdapters/react/useShape.d.ts.map +1 -1
- package/dist/frontendAdapters/react/useShape.js +71 -73
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts +80 -71
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts.map +1 -1
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.js +102 -91
- package/dist/frontendAdapters/svelte/useShape.svelte.d.ts +70 -64
- package/dist/frontendAdapters/svelte/useShape.svelte.d.ts.map +1 -1
- package/dist/frontendAdapters/svelte/useShape.svelte.js +73 -62
- package/dist/frontendAdapters/svelte4/index.d.ts +5 -0
- package/dist/frontendAdapters/svelte4/index.d.ts.map +1 -0
- package/dist/frontendAdapters/svelte4/index.js +12 -0
- package/dist/frontendAdapters/svelte4/useDiscrete.svelte.d.ts +85 -0
- package/dist/frontendAdapters/svelte4/useDiscrete.svelte.d.ts.map +1 -0
- package/dist/frontendAdapters/svelte4/useDiscrete.svelte.js +124 -0
- package/dist/frontendAdapters/svelte4/useShape.svelte.d.ts +76 -0
- package/dist/frontendAdapters/svelte4/useShape.svelte.d.ts.map +1 -0
- package/dist/frontendAdapters/svelte4/useShape.svelte.js +84 -0
- package/dist/frontendAdapters/vue/useDiscrete.d.ts +87 -80
- package/dist/frontendAdapters/vue/useDiscrete.d.ts.map +1 -1
- package/dist/frontendAdapters/vue/useDiscrete.js +96 -84
- package/dist/frontendAdapters/vue/useShape.d.ts +57 -63
- package/dist/frontendAdapters/vue/useShape.d.ts.map +1 -1
- package/dist/frontendAdapters/vue/useShape.js +59 -64
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -3
- package/dist/types.d.ts +17 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +11 -2
- package/package.json +7 -3
- package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts +0 -45
- package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts.map +0 -1
- package/dist/connector/ormConnectionHandler.d.ts +0 -48
- package/dist/connector/ormConnectionHandler.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,34 +1,41 @@
|
|
|
1
|
-
#
|
|
1
|
+
# NextGraph ORM SDK
|
|
2
2
|
|
|
3
|
-
Reactive ORM library for NextGraph
|
|
3
|
+
Reactive ORM library for NextGraph: use reactive (typed) objects that automatically sync to NextGraph's encrypted, local-first storage.
|
|
4
4
|
|
|
5
|
-
For a walk-through
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
For a walk-through you can see the the expense-tracker example apps for [discrete JSON documents](https://git.nextgraph.org/NextGraph/expense-tracker-discrete) or [typed graph documents](https://git.nextgraph.org/NextGraph/expense-tracker-graph).
|
|
6
|
+
|
|
7
|
+
## Why?
|
|
8
|
+
|
|
9
|
+
Different CRDTs have different APIs. We want to make it as easy as possible to use them in the same way:\
|
|
10
|
+
**You modify a plain old TypeScript object and that updates the CRDT.**\
|
|
11
|
+
Vice versa, the CRDT is modified and that is reflected in your TS object.\
|
|
12
|
+
We offer this for **React, Vue, and Svelte**.
|
|
13
|
+
|
|
14
|
+
Note that we support discrete (**JSON**) CRDT and graph (**RDF**) CRDT ORMs.
|
|
15
|
+
|
|
16
|
+
- For graphs, you specify a schema using a SHEX shape and optionally a scope. This provides you with typing support.
|
|
17
|
+
- For discrete CRDTs, all you need is a document id.
|
|
10
18
|
|
|
11
19
|
## Table of Contents
|
|
12
20
|
|
|
13
|
-
- [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- [
|
|
30
|
-
- [
|
|
31
|
-
- [License](#license)
|
|
21
|
+
- [NextGraph ORM SDK](#nextgraph-orm-sdk)
|
|
22
|
+
- [Why?](#why)
|
|
23
|
+
- [Table of Contents](#table-of-contents)
|
|
24
|
+
- [Installation](#installation)
|
|
25
|
+
- [Start](#start)
|
|
26
|
+
- [Graph ORM: Defining Schemas](#graph-orm-defining-schemas)
|
|
27
|
+
- [Framework Usage](#framework-usage)
|
|
28
|
+
- [React](#react)
|
|
29
|
+
- [Vue](#vue)
|
|
30
|
+
- [Svelte](#svelte)
|
|
31
|
+
- [Working with Data](#working-with-data)
|
|
32
|
+
- [Adding Objects](#adding-objects)
|
|
33
|
+
- [Modifying Objects](#modifying-objects)
|
|
34
|
+
- [Deleting Objects](#deleting-objects)
|
|
35
|
+
- [Working with Sets](#working-with-sets)
|
|
36
|
+
- [Relationships](#relationships)
|
|
37
|
+
- [About NextGraph](#about-nextgraph)
|
|
38
|
+
- [License](#license)
|
|
32
39
|
|
|
33
40
|
---
|
|
34
41
|
|
|
@@ -38,7 +45,7 @@ For a walk-through, you can see the the [expense-tracker example app](https://gi
|
|
|
38
45
|
pnpm add @ng-org/orm @ng-org/web @ng-org/alien-deepsignals
|
|
39
46
|
```
|
|
40
47
|
|
|
41
|
-
For schema
|
|
48
|
+
For schema generation, also install:
|
|
42
49
|
|
|
43
50
|
```bash
|
|
44
51
|
pnpm add -D @ng-org/shex-orm
|
|
@@ -46,7 +53,9 @@ pnpm add -D @ng-org/shex-orm
|
|
|
46
53
|
|
|
47
54
|
---
|
|
48
55
|
|
|
49
|
-
##
|
|
56
|
+
## Start
|
|
57
|
+
|
|
58
|
+
You are strongly advised to look at the example apps for [discrete JSON documents](https://git.nextgraph.org/NextGraph/expense-tracker-discrete) and [typed graph documents](https://git.nextgraph.org/NextGraph/expense-tracker-graph).
|
|
50
59
|
|
|
51
60
|
Before using the ORM, initialize NextGraph in your app entry point:
|
|
52
61
|
|
|
@@ -56,6 +65,7 @@ import { initNg } from "@ng-org/orm";
|
|
|
56
65
|
|
|
57
66
|
await init(
|
|
58
67
|
async (event) => {
|
|
68
|
+
// The ORM needs to have access to ng, the interface to the engine running in WASM.
|
|
59
69
|
initNg(ng, event.session);
|
|
60
70
|
},
|
|
61
71
|
true,
|
|
@@ -63,27 +73,24 @@ await init(
|
|
|
63
73
|
);
|
|
64
74
|
```
|
|
65
75
|
|
|
66
|
-
Then use `useShape()`
|
|
76
|
+
Then use `useShape()` for graph, or `useDiscrete()` for discrete documents.
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
import { DogShapeType } from "./shapes/orm/dogShape.shapeTypes";
|
|
78
|
+
In some cases, you may want to use advanced features managing subscriptions with the engine.
|
|
79
|
+
For that, you can directly use:
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
- `OrmSubscription.getOrCreate(ShapeType, scope)` for graphs
|
|
82
|
+
- `DiscreteOrmSubscription.getOrCreate(documentId)` for discrete documents
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
console.log(dog.name);
|
|
77
|
-
}
|
|
78
|
-
```
|
|
84
|
+
Internally, the OrmSubscription keeps a signalObject, a proxied, reactive object. When modifications are made, this makes the frontend components rerender and sends the update to the engine to be persisted.
|
|
85
|
+
In all cases, you have to create a document first with `ng.doc_create()`. For more details, you can consult the example apps and the inline jsdoc documentation.
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
## Defining Schemas
|
|
87
|
+
## Graph ORM: Defining Schemas
|
|
83
88
|
|
|
84
89
|
Define your data model using [SHEX (Shape Expressions)](https://shex.io/):
|
|
90
|
+
See [@ng-org/shex-orm](../shex-orm/README.md) for details.
|
|
85
91
|
|
|
86
92
|
**`shapes/shex/dogShape.shex`**:
|
|
93
|
+
|
|
87
94
|
```shex
|
|
88
95
|
PREFIX ex: <http://example.org/>
|
|
89
96
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
|
@@ -102,15 +109,6 @@ Generate TypeScript types. Add the following to your `package.json` scripts and
|
|
|
102
109
|
"build:orm": "rdf-orm build --input ./src/shapes/shex --output ./src/shapes/orm"
|
|
103
110
|
```
|
|
104
111
|
|
|
105
|
-
This creates:
|
|
106
|
-
- `dogShape.typings.ts` — TypeScript interfaces
|
|
107
|
-
- `dogShape.shapeTypes.ts` — Shape type objects for `useShape()`
|
|
108
|
-
- `dogShape.schema.ts` — Internal schema metadata
|
|
109
|
-
|
|
110
|
-
See [@ng-org/shex-orm](../shex-orm/README.md) for full documentation.
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
112
|
## Framework Usage
|
|
115
113
|
|
|
116
114
|
### React
|
|
@@ -121,16 +119,16 @@ import { DogShapeType } from "./shapes/orm/dogShape.shapeTypes";
|
|
|
121
119
|
import type { Dog } from "./shapes/orm/dogShape.typings";
|
|
122
120
|
|
|
123
121
|
export function DogList() {
|
|
124
|
-
const dogs = useShape(DogShapeType);
|
|
122
|
+
const dogs = useShape(DogShapeType); // DeepSignalSet<Dog>
|
|
125
123
|
|
|
126
124
|
return (
|
|
127
125
|
<ul>
|
|
128
|
-
{[...dogs].map(dog => (
|
|
126
|
+
{[...dogs].map((dog) => (
|
|
129
127
|
<li key={dog["@id"]}>
|
|
130
128
|
{/* Direct mutation triggers re-render */}
|
|
131
129
|
<input
|
|
132
130
|
value={dog.name}
|
|
133
|
-
onChange={e => dog.name = e.target.value}
|
|
131
|
+
onChange={(e) => (dog.name = e.target.value)}
|
|
134
132
|
/>
|
|
135
133
|
</li>
|
|
136
134
|
))}
|
|
@@ -144,25 +142,23 @@ export function DogList() {
|
|
|
144
142
|
### Vue
|
|
145
143
|
|
|
146
144
|
**Parent component** (`DogList.vue`):
|
|
145
|
+
|
|
147
146
|
```vue
|
|
148
147
|
<script setup lang="ts">
|
|
149
148
|
import { useShape } from "@ng-org/orm/vue";
|
|
150
149
|
import { DogShapeType } from "./shapes/orm/dogShape.shapeTypes";
|
|
151
150
|
import DogCard from "./DogCard.vue";
|
|
152
151
|
|
|
153
|
-
const dogs = useShape(DogShapeType);
|
|
152
|
+
const dogs = useShape(DogShapeType); // DeepSignalSet<Dog>
|
|
154
153
|
</script>
|
|
155
154
|
|
|
156
155
|
<template>
|
|
157
|
-
<DogCard
|
|
158
|
-
v-for="dog in dogs"
|
|
159
|
-
:key="dog['@id']"
|
|
160
|
-
:dog="dog"
|
|
161
|
-
/>
|
|
156
|
+
<DogCard v-for="dog in dogs" :key="dog['@id']" :dog="dog" />
|
|
162
157
|
</template>
|
|
163
158
|
```
|
|
164
159
|
|
|
165
160
|
**Child component** (`DogCard.vue`):
|
|
161
|
+
|
|
166
162
|
```vue
|
|
167
163
|
<script setup lang="ts">
|
|
168
164
|
import { useDeepSignal } from "@ng-org/alien-deepsignals/vue";
|
|
@@ -187,14 +183,14 @@ const dog = useDeepSignal(props.dog);
|
|
|
187
183
|
|
|
188
184
|
```svelte
|
|
189
185
|
<script lang="ts">
|
|
190
|
-
import { useShape } from "@ng-org/orm/svelte";
|
|
191
|
-
import { DogShapeType } from "./shapes/orm/dogShape.shapeTypes";
|
|
186
|
+
import { useShape } from "@ng-org/orm/svelte";
|
|
187
|
+
import { DogShapeType } from "./shapes/orm/dogShape.shapeTypes";
|
|
192
188
|
|
|
193
|
-
const dogs = useShape(DogShapeType);
|
|
189
|
+
const dogs = useShape(DogShapeType); // Reactive store
|
|
194
190
|
</script>
|
|
195
191
|
|
|
196
192
|
<ul>
|
|
197
|
-
{#each [...$dogs] as dog (dog[
|
|
193
|
+
{#each [...$dogs] as dog (dog["@id"])}
|
|
198
194
|
<li>
|
|
199
195
|
<input bind:value={dog.name} />
|
|
200
196
|
</li>
|
|
@@ -228,9 +224,9 @@ const docIri = await session.ng.doc_create(
|
|
|
228
224
|
|
|
229
225
|
// Add to the reactive set
|
|
230
226
|
dogs.add({
|
|
231
|
-
"@graph": docIri,
|
|
232
|
-
"@type": "http://example.org/Dog",
|
|
233
|
-
"@id": "",
|
|
227
|
+
"@graph": docIri, // Required: document IRI
|
|
228
|
+
"@type": "http://example.org/Dog", // Required: RDF type
|
|
229
|
+
"@id": "", // Empty = auto-generate subject IRI
|
|
234
230
|
name: "Buddy",
|
|
235
231
|
age: 3,
|
|
236
232
|
toys: new Set(["ball", "rope"]),
|
|
@@ -239,7 +235,7 @@ dogs.add({
|
|
|
239
235
|
|
|
240
236
|
> **Note**: For nested sub-objects, `@graph` is optional — the parent's graph IRI is used.
|
|
241
237
|
>
|
|
242
|
-
> **Note**: If you want to use the ORM signal object in a non-component context, you can create an ORM connection manually using `
|
|
238
|
+
> **Note**: If you want to use the ORM signal object in a non-component context, you can create an ORM connection manually using `OrmSubscription.getOrCreate()`.
|
|
243
239
|
|
|
244
240
|
### Modifying Objects
|
|
245
241
|
|
|
@@ -251,6 +247,7 @@ dog.age = 4;
|
|
|
251
247
|
```
|
|
252
248
|
|
|
253
249
|
Changes are:
|
|
250
|
+
|
|
254
251
|
- Immediately reflected in all components using the same shape
|
|
255
252
|
- Automatically persisted to NextGraph storage
|
|
256
253
|
- Synced to other devices in real-time
|
|
@@ -301,35 +298,20 @@ Link objects by storing the target's `@id` IRI:
|
|
|
301
298
|
dog.owner = person["@id"];
|
|
302
299
|
|
|
303
300
|
// Resolve the relationship
|
|
304
|
-
const owner = people.find(p => p["@id"] === dog.owner);
|
|
301
|
+
const owner = people.find((p) => p["@id"] === dog.owner);
|
|
305
302
|
```
|
|
306
303
|
|
|
307
304
|
---
|
|
308
305
|
|
|
309
|
-
##
|
|
310
|
-
|
|
311
|
-
### `useShape(shapeType)`
|
|
312
|
-
|
|
313
|
-
Returns a `DeepSignalSet<T>` containing all objects of the given shape type.
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
const dogs = useShape(DogShapeType);
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
**DeepSignalSet methods:**
|
|
320
|
-
- `add(obj)` — Add a new object
|
|
321
|
-
- `delete(obj)` — Remove an object
|
|
322
|
-
- `has(obj)` — Check if object exists
|
|
323
|
-
- `size` — Number of objects
|
|
324
|
-
- `getBy(graphIri, subjectIri)` — Find object by IRIs
|
|
325
|
-
- `[Symbol.iterator]` — Iterate with `for...of` or spread `[...set]`
|
|
326
|
-
- ... and all symbol iterator helper methods (like `.map`, `.find`, ...), if you are in an ES2025+ environment.
|
|
327
|
-
|
|
328
|
-
### Shared State
|
|
306
|
+
## About NextGraph
|
|
329
307
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
308
|
+
> **NextGraph** brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
|
|
309
|
+
>
|
|
310
|
+
> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy.
|
|
311
|
+
>
|
|
312
|
+
> Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
|
|
313
|
+
>
|
|
314
|
+
> More info: [https://nextgraph.org](https://nextgraph.org)
|
|
333
315
|
|
|
334
316
|
## License
|
|
335
317
|
|
|
@@ -337,7 +319,7 @@ Licensed under either of
|
|
|
337
319
|
|
|
338
320
|
- Apache License, Version 2.0 ([LICENSE-APACHE2](LICENSE-APACHE2) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
339
321
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
340
|
-
|
|
322
|
+
|
|
341
323
|
at your option.
|
|
342
324
|
|
|
343
325
|
`SPDX-License-Identifier: Apache-2.0 OR MIT`
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
/** @internal */
|
|
1
2
|
export type Patch = {
|
|
2
3
|
/** Property path (array indices, object keys, synthetic Set entry ids) from the root to the mutated location. */
|
|
3
4
|
path: string;
|
|
4
5
|
valType?: string & {};
|
|
5
6
|
value?: unknown;
|
|
6
7
|
} & (SetAddPatch | SetRemovePatch | RemovePatch | LiteralAddPatch);
|
|
8
|
+
/** @internal */
|
|
7
9
|
export interface SetAddPatch {
|
|
8
10
|
/** Mutation kind applied at the resolved `path`. */
|
|
9
11
|
op: "add";
|
|
@@ -15,6 +17,7 @@ export interface SetAddPatch {
|
|
|
15
17
|
*/
|
|
16
18
|
value: number | string | boolean | (number | string | boolean)[];
|
|
17
19
|
}
|
|
20
|
+
/** @internal */
|
|
18
21
|
export interface SetRemovePatch {
|
|
19
22
|
/** Mutation kind applied at the resolved `path`. */
|
|
20
23
|
op: "remove";
|
|
@@ -26,10 +29,12 @@ export interface SetRemovePatch {
|
|
|
26
29
|
*/
|
|
27
30
|
value: number | string | boolean | object | (number | string | boolean | object)[];
|
|
28
31
|
}
|
|
32
|
+
/** @internal */
|
|
29
33
|
export interface RemovePatch {
|
|
30
34
|
/** Mutation kind applied at the resolved `path`. */
|
|
31
35
|
op: "remove";
|
|
32
36
|
}
|
|
37
|
+
/** @internal */
|
|
33
38
|
export interface LiteralAddPatch {
|
|
34
39
|
/** Mutation kind applied at the resolved `path`. */
|
|
35
40
|
op: "add";
|
|
@@ -37,17 +42,19 @@ export interface LiteralAddPatch {
|
|
|
37
42
|
value: string | number | boolean | object;
|
|
38
43
|
}
|
|
39
44
|
/**
|
|
45
|
+
* @internal
|
|
46
|
+
*
|
|
40
47
|
* Apply a diff to an object.
|
|
41
48
|
*
|
|
42
49
|
* The syntax is inspired by RFC 6902 but it is not compatible.
|
|
43
50
|
*
|
|
44
51
|
* It supports Sets for multi-valued properties:
|
|
45
52
|
* - Primitive values are added as Sets (Set<string | number | boolean>)
|
|
46
|
-
* - Multi-valued objects are stored in Sets, accessed by their
|
|
47
|
-
* - Single objects are plain objects with an
|
|
53
|
+
* - Multi-valued objects are stored in Sets, accessed by their `@id` property
|
|
54
|
+
* - Single objects are plain objects with an `@id` property
|
|
48
55
|
*
|
|
49
56
|
* Path traversal:
|
|
50
|
-
* - When traversing through a Set, the path segment is treated as an
|
|
57
|
+
* - When traversing through a Set, the path segment is treated as an `@id` to find the object
|
|
51
58
|
* - When traversing through a plain object, the path segment is a property name
|
|
52
59
|
*
|
|
53
60
|
* @example operations
|
|
@@ -88,13 +95,15 @@ export interface LiteralAddPatch {
|
|
|
88
95
|
* @param patches An array of patches to apply to the object.
|
|
89
96
|
* @param ensurePathExists If true, create nested objects along the path if the path does not exist.
|
|
90
97
|
*
|
|
91
|
-
*
|
|
98
|
+
* Note: When creating new objects, this function pre-scans upcoming patches to find `@id` and `@graph`
|
|
92
99
|
* values that will be assigned to the object. This prevents the signal library's propGenerator
|
|
93
100
|
* from being triggered before these identity fields are set, which would cause it to generate
|
|
94
101
|
* random IDs unnecessarily.
|
|
95
102
|
*/
|
|
96
103
|
export declare function applyPatches(currentState: Record<string, any>, patches: Patch[], ormType: "set" | "discrete", ensurePathExists?: boolean): void;
|
|
97
104
|
/**
|
|
105
|
+
* @internal
|
|
106
|
+
*
|
|
98
107
|
* See documentation for applyPatches
|
|
99
108
|
*/
|
|
100
109
|
export declare function applyPatchesToDeepSignal(currentState: object, patch: Patch[], ormType: "set" | "discrete"): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"applyPatches.d.ts","sourceRoot":"","sources":["../../src/connector/applyPatches.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,KAAK,GAAG;IAChB,iHAAiH;IACjH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,CAAC,WAAW,GAAG,cAAc,GAAG,WAAW,GAAG,eAAe,CAAC,CAAC;AAEnE,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,KAAK,CAAC;IACf;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CACpE;AAED,MAAM,WAAW,cAAc;IAC3B,oDAAoD;IACpD,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;IACf;;;;OAIG;IACH,KAAK,EACC,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,EAAE,EAAE,QAAQ,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC5B,oDAAoD;IACpD,EAAE,EAAE,KAAK,CAAC;IACV,2DAA2D;IAC3D,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;CAC7C;AAyCD
|
|
1
|
+
{"version":3,"file":"applyPatches.d.ts","sourceRoot":"","sources":["../../src/connector/applyPatches.ts"],"names":[],"mappings":"AAYA,gBAAgB;AAChB,MAAM,MAAM,KAAK,GAAG;IAChB,iHAAiH;IACjH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,CAAC,WAAW,GAAG,cAAc,GAAG,WAAW,GAAG,eAAe,CAAC,CAAC;AAEnE,gBAAgB;AAChB,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,KAAK,CAAC;IACf;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CACpE;AAED,gBAAgB;AAChB,MAAM,WAAW,cAAc;IAC3B,oDAAoD;IACpD,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;IACf;;;;OAIG;IACH,KAAK,EACC,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC;CAChD;AAED,gBAAgB;AAChB,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,EAAE,EAAE,QAAQ,CAAC;CAChB;AAED,gBAAgB;AAChB,MAAM,WAAW,eAAe;IAC5B,oDAAoD;IACpD,EAAE,EAAE,KAAK,CAAC;IACV,2DAA2D;IAC3D,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;CAC7C;AAyCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,wBAAgB,YAAY,CACxB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,OAAO,EAAE,KAAK,EAAE,EAChB,OAAO,EAAE,KAAK,GAAG,UAAU,EAC3B,gBAAgB,GAAE,OAAe,QA6PpC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACpC,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,KAAK,EAAE,EACd,OAAO,EAAE,KAAK,GAAG,UAAU,QAU9B"}
|
|
@@ -45,17 +45,19 @@ function findInSetBySegment(set, seg) {
|
|
|
45
45
|
return undefined;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
|
+
* @internal
|
|
49
|
+
*
|
|
48
50
|
* Apply a diff to an object.
|
|
49
51
|
*
|
|
50
52
|
* The syntax is inspired by RFC 6902 but it is not compatible.
|
|
51
53
|
*
|
|
52
54
|
* It supports Sets for multi-valued properties:
|
|
53
55
|
* - Primitive values are added as Sets (Set<string | number | boolean>)
|
|
54
|
-
* - Multi-valued objects are stored in Sets, accessed by their
|
|
55
|
-
* - Single objects are plain objects with an
|
|
56
|
+
* - Multi-valued objects are stored in Sets, accessed by their `@id` property
|
|
57
|
+
* - Single objects are plain objects with an `@id` property
|
|
56
58
|
*
|
|
57
59
|
* Path traversal:
|
|
58
|
-
* - When traversing through a Set, the path segment is treated as an
|
|
60
|
+
* - When traversing through a Set, the path segment is treated as an `@id` to find the object
|
|
59
61
|
* - When traversing through a plain object, the path segment is a property name
|
|
60
62
|
*
|
|
61
63
|
* @example operations
|
|
@@ -96,7 +98,7 @@ function findInSetBySegment(set, seg) {
|
|
|
96
98
|
* @param patches An array of patches to apply to the object.
|
|
97
99
|
* @param ensurePathExists If true, create nested objects along the path if the path does not exist.
|
|
98
100
|
*
|
|
99
|
-
*
|
|
101
|
+
* Note: When creating new objects, this function pre-scans upcoming patches to find `@id` and `@graph`
|
|
100
102
|
* values that will be assigned to the object. This prevents the signal library's propGenerator
|
|
101
103
|
* from being triggered before these identity fields are set, which would cause it to generate
|
|
102
104
|
* random IDs unnecessarily.
|
|
@@ -273,7 +275,7 @@ export function applyPatches(currentState, patches, ormType, ensurePathExists =
|
|
|
273
275
|
if (patch.op === "add" &&
|
|
274
276
|
typeof patch.value === "object" &&
|
|
275
277
|
patch.value !== null &&
|
|
276
|
-
ormType === "set" // TODO: The
|
|
278
|
+
ormType === "set" // TODO: The engine should preferably add valType: "set" here (we don't need ormType then).
|
|
277
279
|
) {
|
|
278
280
|
const leafVal = parentVal[key];
|
|
279
281
|
const hasId = patches.at(patchIndex + 2)?.path.endsWith("@id");
|
|
@@ -334,6 +336,8 @@ export function applyPatches(currentState, patches, ormType, ensurePathExists =
|
|
|
334
336
|
}
|
|
335
337
|
}
|
|
336
338
|
/**
|
|
339
|
+
* @internal
|
|
340
|
+
*
|
|
337
341
|
* See documentation for applyPatches
|
|
338
342
|
*/
|
|
339
343
|
export function applyPatchesToDeepSignal(currentState, patch, ormType) {
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { DiscreteArray, DiscreteObject } from "../../types.ts";
|
|
2
|
+
import { Patch } from "../applyPatches.ts";
|
|
3
|
+
import type { DeepPatch, DeepSignal } from "@ng-org/alien-deepsignals";
|
|
4
|
+
import type { 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](../../../README.md) and follow the tutorial.
|
|
14
|
+
*/
|
|
15
|
+
export declare class DiscreteOrmSubscription {
|
|
16
|
+
/** Global store of all subscriptions. We use that for pooling. */
|
|
17
|
+
private static idToEntry;
|
|
18
|
+
/** The document id (IRI) of the subscribed document. */
|
|
19
|
+
readonly documentId: string;
|
|
20
|
+
private _signalObject;
|
|
21
|
+
private stopSignalListening;
|
|
22
|
+
/** The subscription id kept as an identifier for communicating with the verifier. */
|
|
23
|
+
private subscriptionId;
|
|
24
|
+
/** The number of OrmSubscriptions with the same shape and scope (for pooling). */
|
|
25
|
+
private refCount;
|
|
26
|
+
/** When true, modifications from the signalObject are not processed. */
|
|
27
|
+
suspendDeepWatcher: boolean;
|
|
28
|
+
/** True, if a transaction is running. */
|
|
29
|
+
inTransaction: boolean;
|
|
30
|
+
/** Aggregation of patches to be sent when in transaction. */
|
|
31
|
+
pendingPatches: Patch[] | undefined;
|
|
32
|
+
/** **Await to ensure that the subscription is established and the data arrived.** */
|
|
33
|
+
readyPromise: Promise<void>;
|
|
34
|
+
private closeOrmSubscription;
|
|
35
|
+
/** Function to call once initial data has been applied. */
|
|
36
|
+
private resolveReady;
|
|
37
|
+
private constructor();
|
|
38
|
+
get signalObject(): DeepSignal<DiscreteArray | DiscreteObject> | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Returns an OrmSubscription which subscribes to the given
|
|
41
|
+
* document in a 2-way binding.
|
|
42
|
+
*
|
|
43
|
+
* You **find the document data** in the **`signalObject`**
|
|
44
|
+
* once {@link readyPromise} resolves.
|
|
45
|
+
* This is a {@link DeepSignal} object or array, depending on
|
|
46
|
+
* your CRDT document (e.g. YArray vs YMap). The signalObject
|
|
47
|
+
* behaves like a regular set to the outside but has a couple
|
|
48
|
+
* of additional features:
|
|
49
|
+
* - Modifications are propagated back to the document.
|
|
50
|
+
* Note that multiple immediate modifications in the same task,
|
|
51
|
+
* e.g. `obj[0] = "foo"; obj[1] = "bar"` are batched together
|
|
52
|
+
* and sent in a subsequent microtask.
|
|
53
|
+
* - External document changes are immediately reflected in the object.
|
|
54
|
+
* - Watch for object changes using {@link watchDeepSignal}.
|
|
55
|
+
*
|
|
56
|
+
* You can use **transactions**, to prevent excessive calls to the engine
|
|
57
|
+
* with {@link beginTransaction} and {@link commitTransaction}.
|
|
58
|
+
*
|
|
59
|
+
* In many cases, you are advised to use a hook for your
|
|
60
|
+
* favorite framework under `@ng-org/orm/react|vue|svelte`
|
|
61
|
+
* instead of calling `getOrCreate` directly.
|
|
62
|
+
*
|
|
63
|
+
* Call `{@link close}, to close the subscription.
|
|
64
|
+
*
|
|
65
|
+
* Note: If another call to `getOrCreate` was previously made
|
|
66
|
+
* and {@link close} was not called on it (or only shortly after),
|
|
67
|
+
* it will return the same OrmSubscription.
|
|
68
|
+
*
|
|
69
|
+
* @param documentId The document ID (IRI) of the CRDT
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* // We assume you have created a CRDT document already, as below.
|
|
74
|
+
* // const documentId = await ng.doc_create(
|
|
75
|
+
* // session_id,
|
|
76
|
+
* // crdt, // "Automerge" | "YMap" | "YArray". YArray is for root arrays, the other two have objects at root.
|
|
77
|
+
* // crdt === "Automerge" ? "data:json" : crdt === "YMap ? "data:map" : "data:array",
|
|
78
|
+
* // "store",
|
|
79
|
+
* // undefined
|
|
80
|
+
* // );
|
|
81
|
+
* const subscription = DiscreteOrmSubscription.getOrCreate(documentId);
|
|
82
|
+
* // Wait for data.
|
|
83
|
+
* await subscription.readyPromise;
|
|
84
|
+
*
|
|
85
|
+
* const document = subscription.signalObject;
|
|
86
|
+
* if (!document.expenses) {
|
|
87
|
+
* document.expenses = [];
|
|
88
|
+
* }
|
|
89
|
+
* document.expenses.push({
|
|
90
|
+
* name: "New Expense name",
|
|
91
|
+
* description: "Expense description"
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
94
|
+
* // Await promise to run the below code in a new task.
|
|
95
|
+
* // That will push the changes to the database.
|
|
96
|
+
* await Promise.resolve();
|
|
97
|
+
*
|
|
98
|
+
* // Here, the expense modifications have been have been committed
|
|
99
|
+
* // (unless you had previously called subscription.beginTransaction()).
|
|
100
|
+
* // The data is available in subscriptions running on a different device too.
|
|
101
|
+
*
|
|
102
|
+
* subscription.close();
|
|
103
|
+
*
|
|
104
|
+
* // If you create a new subscription with the same document within a couple of 100ms,
|
|
105
|
+
* // The subscription hasn't been closed and the old one is returned so that the data
|
|
106
|
+
* // is available instantly. This is especially useful in the context of frontend frameworks.
|
|
107
|
+
* const subscription2 = DiscreteOrmSubscription.getOrCreate(documentId);
|
|
108
|
+
*
|
|
109
|
+
* subscription2.signalObject.expenses.push({
|
|
110
|
+
* name: "Second expense",
|
|
111
|
+
* description: "Second description"
|
|
112
|
+
* });
|
|
113
|
+
*
|
|
114
|
+
* subscription2.close();
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
static getOrCreate: <T extends BaseType>(documentId: string) => DiscreteOrmSubscription;
|
|
118
|
+
/**
|
|
119
|
+
* Stop the subscription.
|
|
120
|
+
*
|
|
121
|
+
* **If there is more than one subscription with the same shape type and scope
|
|
122
|
+
* the orm subscription will persist.**
|
|
123
|
+
*
|
|
124
|
+
* Additionally, the closing of the subscription is delayed by a couple hundred milliseconds
|
|
125
|
+
* so that when frontend frameworks unmount and soon mound a component again with the same
|
|
126
|
+
* shape type and scope, no new orm subscription has be set up with the engine.
|
|
127
|
+
*/
|
|
128
|
+
close: () => void;
|
|
129
|
+
/** Handle updates (patches) coming from signal object modifications. */
|
|
130
|
+
private onSignalObjectUpdate;
|
|
131
|
+
/** Handle messages coming from the engine (initial data or patches). */
|
|
132
|
+
private onBackendMessage;
|
|
133
|
+
private handleInitialResponse;
|
|
134
|
+
/** Handle incoming patches from the engine */
|
|
135
|
+
private onBackendUpdate;
|
|
136
|
+
/**
|
|
137
|
+
* Begins a transaction that batches changes to be committed to the database.
|
|
138
|
+
* This is useful for performance reasons.
|
|
139
|
+
*
|
|
140
|
+
* Note that this does not disable reactivity of the `signalObject`.
|
|
141
|
+
* Modifications keep being rendered.
|
|
142
|
+
*/
|
|
143
|
+
beginTransaction: () => void;
|
|
144
|
+
/**
|
|
145
|
+
* Commits a transactions sending all modifications made during the transaction
|
|
146
|
+
* (started with `beginTransaction`) to the database.
|
|
147
|
+
*/
|
|
148
|
+
commitTransaction: () => Promise<void>;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Converts DeepSignal patches to ORM Wasm-compatible patches
|
|
152
|
+
* @param patches DeepSignal patches
|
|
153
|
+
* @returns Patches with stringified path
|
|
154
|
+
*/
|
|
155
|
+
export declare function deepPatchesToWasm(patches: DeepPatch[]): Patch[];
|
|
156
|
+
//# sourceMappingURL=discreteOrmSubscriptionHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discreteOrmSubscriptionHandler.d.ts","sourceRoot":"","sources":["../../../src/connector/discrete/discreteOrmSubscriptionHandler.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAA4B,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAQrE,OAAO,KAAK,EACR,SAAS,EACT,UAAU,EAEb,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AASjD;;;;;;;;;GASG;AACH,qBAAa,uBAAuB;IAChC,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,SAAS,CAA8C;IAEtE,wDAAwD;IACxD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,aAAa,CAEL;IAChB,OAAO,CAAC,mBAAmB,CAA2B;IACtD,qFAAqF;IACrF,OAAO,CAAC,cAAc,CAAqB;IAC3C,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAS;IACzB,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;IAElC,OAAO;IA+BP,IAAW,YAAY,2DAEtB;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6EG;IACH,OAAc,WAAW,GAAI,CAAC,SAAS,QAAQ,EAC3C,YAAY,MAAM,KACnB,uBAAuB,CAcxB;IAEF;;;;;;;;;OASG;IAEI,KAAK,aASV;IAEF,wEAAwE;IACxE,OAAO,CAAC,oBAAoB,CAsB1B;IAEF,wEAAwE;IACxE,OAAO,CAAC,gBAAgB,CAStB;IAEF,OAAO,CAAC,qBAAqB,CAiB3B;IAEF,8CAA8C;IAC9C,OAAO,CAAC,eAAe,CAUrB;IAEF;;;;;;OAMG;IACI,gBAAgB,aAerB;IAEF;;;OAGG;IACI,iBAAiB,sBAiCtB;CACL;AAED;;;;GAIG;AAEH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,CAO/D"}
|