@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
package/README.md
CHANGED
|
@@ -1,44 +1,45 @@
|
|
|
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
|
+
## Reference documentation
|
|
8
|
+
|
|
9
|
+
[Reference documentation is available here on docs.nextgraph.org](https://docs.nextgraph.org/en/reference/orm/).
|
|
10
|
+
|
|
11
|
+
## Why?
|
|
12
|
+
|
|
13
|
+
Different CRDTs have different APIs. We want to make it as easy as possible to use them in the same way:\
|
|
14
|
+
**You modify a plain old TypeScript object and that updates the CRDT.**\
|
|
15
|
+
Vice versa, the CRDT is modified and that is reflected in your TS object.\
|
|
16
|
+
We offer this for **React, Vue, and Svelte**.
|
|
17
|
+
|
|
18
|
+
Note that we support discrete (**JSON**) CRDT and graph (**RDF**) CRDT ORMs.
|
|
19
|
+
|
|
20
|
+
- For graphs, you specify a schema using a SHEX shape and optionally a scope. This provides you with typing support.
|
|
21
|
+
- For discrete CRDTs, all you need is a document ID (NURI).
|
|
10
22
|
|
|
11
23
|
## Table of Contents
|
|
12
24
|
|
|
13
|
-
- [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- [
|
|
20
|
-
|
|
21
|
-
- [Svelte](#svelte)
|
|
22
|
-
- [Working with Data](#working-with-data)
|
|
23
|
-
- [Adding Objects](#adding-objects)
|
|
24
|
-
- [Modifying Objects](#modifying-objects)
|
|
25
|
-
- [Deleting Objects](#deleting-objects)
|
|
26
|
-
- [Working with Sets](#working-with-sets)
|
|
27
|
-
- [Relationships](#relationships)
|
|
28
|
-
- [API Reference](#api-reference)
|
|
29
|
-
- [`useShape(shapeType)`](#useshapeshapetype)
|
|
30
|
-
- [Shared State](#shared-state)
|
|
31
|
-
- [License](#license)
|
|
25
|
+
- [Installation](#installation)
|
|
26
|
+
- [Start](#start)
|
|
27
|
+
- [Graph ORM: Defining Schemas](#graph-orm-defining-schemas)
|
|
28
|
+
- [Frontend Framework Usage](#frontend-framework-usage)
|
|
29
|
+
- [Working with Data](#working-with-data)
|
|
30
|
+
- [Creating a Document](#creating-a-document)
|
|
31
|
+
- [Using and Modifying ORM Objects](#using-and-modifying-orm-objects)
|
|
32
|
+
- [Graph ORM: Relationships](#graph-orm-relationships)
|
|
32
33
|
|
|
33
34
|
---
|
|
34
35
|
|
|
35
36
|
## Installation
|
|
36
37
|
|
|
37
38
|
```bash
|
|
38
|
-
pnpm add @ng-org/orm @ng-org/web
|
|
39
|
+
pnpm add @ng-org/orm @ng-org/web
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
For schema
|
|
42
|
+
For schema generation, also install:
|
|
42
43
|
|
|
43
44
|
```bash
|
|
44
45
|
pnpm add -D @ng-org/shex-orm
|
|
@@ -46,7 +47,9 @@ pnpm add -D @ng-org/shex-orm
|
|
|
46
47
|
|
|
47
48
|
---
|
|
48
49
|
|
|
49
|
-
##
|
|
50
|
+
## Start
|
|
51
|
+
|
|
52
|
+
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
53
|
|
|
51
54
|
Before using the ORM, initialize NextGraph in your app entry point:
|
|
52
55
|
|
|
@@ -56,6 +59,7 @@ import { initNg } from "@ng-org/orm";
|
|
|
56
59
|
|
|
57
60
|
await init(
|
|
58
61
|
async (event) => {
|
|
62
|
+
// The ORM needs to have access to ng, the interface to the engine running in WASM.
|
|
59
63
|
initNg(ng, event.session);
|
|
60
64
|
},
|
|
61
65
|
true,
|
|
@@ -63,27 +67,25 @@ await init(
|
|
|
63
67
|
);
|
|
64
68
|
```
|
|
65
69
|
|
|
66
|
-
Then use `useShape()`
|
|
70
|
+
Then use `useShape()` for graphs, or `useDiscrete()` for discrete documents.
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
In some cases, you may want to use advanced features managing subscriptions with the engine.
|
|
73
|
+
With an OrmSubscription, you can manage things like transactions manually.
|
|
74
|
+
This is useful for example when you want to manage a state across components.
|
|
75
|
+
See [`OrmSubscription.getOrCreate(ShapeType, scope)`](#getorcreate-1) for graphs
|
|
76
|
+
and [`DiscreteOrmSubscription.getOrCreate(documentId)`](#getorcreate) for discrete documents.
|
|
71
77
|
|
|
72
|
-
|
|
78
|
+
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.
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
for (const dog of dogs) {
|
|
76
|
-
console.log(dog.name);
|
|
77
|
-
}
|
|
78
|
-
```
|
|
80
|
+
In all cases, you have to create a document first with `ng.doc_create()`.
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
## Defining Schemas
|
|
82
|
+
## Graph ORM: Defining Schemas
|
|
83
83
|
|
|
84
84
|
Define your data model using [SHEX (Shape Expressions)](https://shex.io/):
|
|
85
|
+
See [@ng-org/shex-orm](../shex-orm/README.md) for details.
|
|
85
86
|
|
|
86
87
|
**`shapes/shex/dogShape.shex`**:
|
|
88
|
+
|
|
87
89
|
```shex
|
|
88
90
|
PREFIX ex: <http://example.org/>
|
|
89
91
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
|
@@ -99,237 +101,156 @@ ex:Dog {
|
|
|
99
101
|
Generate TypeScript types. Add the following to your `package.json` scripts and run `build:orm`:
|
|
100
102
|
|
|
101
103
|
```json
|
|
102
|
-
|
|
104
|
+
"build:orm": "rdf-orm build --input ./src/shapes/shex --output ./src/shapes/orm"
|
|
103
105
|
```
|
|
104
106
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<input
|
|
132
|
-
value={dog.name}
|
|
133
|
-
onChange={e => dog.name = e.target.value}
|
|
134
|
-
/>
|
|
135
|
-
</li>
|
|
136
|
-
))}
|
|
137
|
-
</ul>
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
> **Note**: No `setState` needed — just mutate the object directly.
|
|
143
|
-
|
|
144
|
-
### Vue
|
|
145
|
-
|
|
146
|
-
**Parent component** (`DogList.vue`):
|
|
147
|
-
```vue
|
|
148
|
-
<script setup lang="ts">
|
|
149
|
-
import { useShape } from "@ng-org/orm/vue";
|
|
150
|
-
import { DogShapeType } from "./shapes/orm/dogShape.shapeTypes";
|
|
151
|
-
import DogCard from "./DogCard.vue";
|
|
152
|
-
|
|
153
|
-
const dogs = useShape(DogShapeType); // DeepSignalSet<Dog>
|
|
154
|
-
</script>
|
|
155
|
-
|
|
156
|
-
<template>
|
|
157
|
-
<DogCard
|
|
158
|
-
v-for="dog in dogs"
|
|
159
|
-
:key="dog['@id']"
|
|
160
|
-
:dog="dog"
|
|
161
|
-
/>
|
|
162
|
-
</template>
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
**Child component** (`DogCard.vue`):
|
|
166
|
-
```vue
|
|
167
|
-
<script setup lang="ts">
|
|
168
|
-
import { useDeepSignal } from "@ng-org/alien-deepsignals/vue";
|
|
169
|
-
import type { Dog } from "./shapes/orm/dogShape.typings";
|
|
170
|
-
|
|
171
|
-
const props = defineProps<{ dog: Dog }>();
|
|
172
|
-
|
|
173
|
-
// Required for reactivity in child components!
|
|
174
|
-
const dog = useDeepSignal(props.dog);
|
|
175
|
-
</script>
|
|
176
|
-
|
|
177
|
-
<template>
|
|
178
|
-
<div>
|
|
179
|
-
<input v-model="dog.name" />
|
|
180
|
-
</div>
|
|
181
|
-
</template>
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
> **Important**: In Vue child components, wrap props with `useDeepSignal()` to enable reactivity.
|
|
185
|
-
|
|
186
|
-
### Svelte
|
|
187
|
-
|
|
188
|
-
```svelte
|
|
189
|
-
<script lang="ts">
|
|
190
|
-
import { useShape } from "@ng-org/orm/svelte";
|
|
191
|
-
import { DogShapeType } from "./shapes/orm/dogShape.shapeTypes";
|
|
192
|
-
|
|
193
|
-
const dogs = useShape(DogShapeType); // Reactive store
|
|
194
|
-
</script>
|
|
107
|
+
## Frontend Framework Usage
|
|
108
|
+
|
|
109
|
+
The SDK offers hooks for discrete and graph-based CRDTs for Svelte, Vue and React:
|
|
110
|
+
|
|
111
|
+
- discrete CRDTs for
|
|
112
|
+
- Svelte 5: [useDiscrete](#svelteusediscrete)
|
|
113
|
+
- Svelte 3/4: [useDiscrete](#svelte4usediscrete)
|
|
114
|
+
- Vue: [useDiscrete](#vueusediscrete)
|
|
115
|
+
- React: [useDiscrete](#reactusediscrete)
|
|
116
|
+
- graph CRDTs for:
|
|
117
|
+
- Svelte 5: [useShape](#svelteuseshape)
|
|
118
|
+
- Svelte 3/4: [useShape](#svelte4useshape)
|
|
119
|
+
- Vue: [useShape](#vueuseshape)
|
|
120
|
+
- React: [useShape](#reactuseshape)
|
|
121
|
+
|
|
122
|
+
All of them have the same logic. They create a 2-way binding to the engine.
|
|
123
|
+
You can modify the returned object like any other JSON object. Changes are immediately
|
|
124
|
+
reflected in the CRDT and the components rerender.
|
|
125
|
+
When the component unmounts, the subscription is closed.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
// Queries the graphs with NURI did:ng:o:g1 and did:ng:o:g2 and with subject s1 or s2.
|
|
129
|
+
const expenses: DeepSignal<Set<Expense>> = useShape(ExpenseShapeType, {
|
|
130
|
+
graphs: ["did:ng:o:g1", "did:ng:o:g2"],
|
|
131
|
+
subjects: ["<s1 IRI>", "<s2 IRI>"],
|
|
132
|
+
});
|
|
195
133
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
<input bind:value={dog.name} />
|
|
200
|
-
</li>
|
|
201
|
-
{/each}
|
|
202
|
-
</ul>
|
|
134
|
+
// Use expenses in your component
|
|
135
|
+
// and modify them to trigger a rerender and persist them.
|
|
136
|
+
// ...
|
|
203
137
|
```
|
|
204
138
|
|
|
205
|
-
> **Note**: Access the store value with `$dogs`. Standard Svelte binding works.
|
|
206
|
-
|
|
207
139
|
---
|
|
208
140
|
|
|
209
141
|
## Working with Data
|
|
210
142
|
|
|
211
|
-
|
|
143
|
+
The ORM is designed to make working with data as normal as possible.
|
|
144
|
+
You get an object as you are used to it and when you change properties,
|
|
145
|
+
they are automatically persisted and synced with other devices. Conversely,
|
|
146
|
+
modifications arrive at the ORM objects immediately and your components rerender.
|
|
212
147
|
|
|
213
|
-
|
|
148
|
+
### Creating a Document
|
|
214
149
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const session = await sessionPromise;
|
|
150
|
+
First, you need a document to store and get your data.
|
|
151
|
+
With the document NURI, you can then create ORM objects.
|
|
219
152
|
|
|
153
|
+
```ts
|
|
220
154
|
// Create a new NextGraph document
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
"Graph",
|
|
224
|
-
"data:graph",
|
|
155
|
+
const docNuri = await ng.doc_create(
|
|
156
|
+
session_id,
|
|
157
|
+
"Graph", // Or "YMap" or "Automerge", for discrete
|
|
158
|
+
"data:graph", // Or "data:json" : "data:map" for Automerge or YJs
|
|
225
159
|
"store",
|
|
226
160
|
undefined
|
|
227
161
|
);
|
|
228
162
|
|
|
229
|
-
// Add to the
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
age: 3,
|
|
236
|
-
toys: new Set(["ball", "rope"]),
|
|
237
|
-
});
|
|
163
|
+
// Add class to RDF part of the document so we can find it again.
|
|
164
|
+
await ng.sparql_update(
|
|
165
|
+
session_id,
|
|
166
|
+
`INSERT DATA { GRAPH <${documentId}> {<${documentId}> a <${APPLICATION_CLASS_IRI}> } }`,
|
|
167
|
+
documentId
|
|
168
|
+
);
|
|
238
169
|
```
|
|
239
170
|
|
|
240
|
-
|
|
241
|
-
>
|
|
242
|
-
> **Note**: If you want to use the ORM signal object in a non-component context, you can use `createSignalObjectForShape` function as well.
|
|
243
|
-
|
|
244
|
-
### Modifying Objects
|
|
171
|
+
To find your document, you can make a sparql query as well:
|
|
245
172
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
173
|
+
```ts
|
|
174
|
+
const ret = await ng.sparql_query(
|
|
175
|
+
session_id,
|
|
176
|
+
`SELECT ?storeId WHERE { GRAPH ?storeId { ?s a <${APPLICATION_CLASS_IRI}> } }`,
|
|
177
|
+
undefined,
|
|
178
|
+
undefined
|
|
179
|
+
);
|
|
180
|
+
let documentId = ret?.results.bindings?.[0]?.storeId?.value;
|
|
251
181
|
```
|
|
252
182
|
|
|
253
|
-
|
|
254
|
-
- Immediately reflected in all components using the same shape
|
|
255
|
-
- Automatically persisted to NextGraph storage
|
|
256
|
-
- Synced to other devices in real-time
|
|
257
|
-
|
|
258
|
-
### Deleting Objects
|
|
183
|
+
### Using and Modifying ORM Objects
|
|
259
184
|
|
|
260
|
-
|
|
261
|
-
dogs.delete(dog);
|
|
262
|
-
```
|
|
185
|
+
There are multiple ways to get and modify data:
|
|
263
186
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
187
|
+
- Get and modify the data returned by a `useShape()` or `useDiscrete()` hook inside a component.
|
|
188
|
+
- Get and modify the signalObject of the subscription returned by `Orm(Discrete)Subscription.getOrCreate()`.
|
|
189
|
+
- For graph ORMs: Call [`insertObject()`](#insertobject) or [`getObjects`](#getobjects) (no 2-way binding).
|
|
267
190
|
|
|
268
191
|
```typescript
|
|
269
|
-
|
|
270
|
-
|
|
192
|
+
const dogSubscription = OrmSubscription.getOrCreate(DogShape, {
|
|
193
|
+
graphs: [docNuri],
|
|
194
|
+
});
|
|
195
|
+
await dogSubscription.readyPromise;
|
|
271
196
|
|
|
272
|
-
//
|
|
273
|
-
|
|
197
|
+
// If we used OrmDiscreteSubscription, the signalObject type would be array or object.
|
|
198
|
+
const dogSet: DeepSignal<Set<Dog>> = dogSubscription.signalObject;
|
|
274
199
|
|
|
275
|
-
|
|
276
|
-
|
|
200
|
+
dogs.add({
|
|
201
|
+
// Required: The document NURI. May be set to `""` for nested objects (will be inherited from parent object then).
|
|
202
|
+
"@graph": docNuri,
|
|
203
|
+
"@type": "did:ng:x:Dog", // Required: RDF type
|
|
204
|
+
"@id": "", // Empty string = auto-generate subject IRI
|
|
205
|
+
name: "Mr Puppy",
|
|
206
|
+
age: 2,
|
|
207
|
+
toys: new Set(["ball", "rope"]),
|
|
208
|
+
});
|
|
277
209
|
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
210
|
+
// When you know that only one element is in the set, you can call `.first()` to get it.
|
|
211
|
+
const aDog = dogs.first();
|
|
212
|
+
aDog.age += 1;
|
|
213
|
+
aDog.toy.add("bone");
|
|
282
214
|
|
|
283
|
-
//
|
|
284
|
-
|
|
215
|
+
// Utility to find objects in sets:
|
|
216
|
+
const sameDog = dogs.getBy(aDog["@graph"], aDog["@id"]);
|
|
217
|
+
// sameSog === aDog.
|
|
285
218
|
|
|
286
|
-
|
|
287
|
-
dogs.forEach((dog) => {
|
|
288
|
-
console.log(dog.toys.size);
|
|
289
|
-
});
|
|
219
|
+
dogs.delete(aDog);
|
|
290
220
|
```
|
|
291
221
|
|
|
292
|
-
|
|
222
|
+
Note that the graph CRDT supports sets only, the discrete CRDTs arrays only.
|
|
293
223
|
|
|
294
|
-
|
|
224
|
+
#### Graph ORM: Relationships
|
|
225
|
+
|
|
226
|
+
To reference external objects, you can use their `@id`.
|
|
295
227
|
|
|
296
228
|
```typescript
|
|
297
|
-
|
|
298
|
-
// ex:owner IRI ;
|
|
229
|
+
casey.friends.add(jackNuri);
|
|
299
230
|
|
|
300
|
-
//
|
|
301
|
-
|
|
231
|
+
// When the child object is a nested object that you do not have in memory,
|
|
232
|
+
// you can establish the link by adding an object that contains the `@id` property only.
|
|
233
|
+
shoppingExpense.category.add({ "@id": "<Subject IRI of expense category>" });
|
|
234
|
+
// Link objects by storing the target's `@id` NURI/IRI:
|
|
302
235
|
|
|
236
|
+
dog.owner = jackNuri;
|
|
303
237
|
// Resolve the relationship
|
|
304
|
-
const
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
---
|
|
308
|
-
|
|
309
|
-
## API Reference
|
|
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);
|
|
238
|
+
const jack = people.find((p) => p["@id"] === dog.owner);
|
|
317
239
|
```
|
|
318
240
|
|
|
319
|
-
|
|
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.
|
|
241
|
+
Note that when you delete a nested object from a parent, _only the linkage_ to it is removed. The nested object itself (its quads) are not deleted.
|
|
327
242
|
|
|
328
|
-
|
|
243
|
+
---
|
|
329
244
|
|
|
330
|
-
|
|
245
|
+
## About NextGraph
|
|
331
246
|
|
|
332
|
-
|
|
247
|
+
> **NextGraph** brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
|
|
248
|
+
>
|
|
249
|
+
> 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.
|
|
250
|
+
>
|
|
251
|
+
> 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.
|
|
252
|
+
>
|
|
253
|
+
> More info: [https://nextgraph.org](https://nextgraph.org)
|
|
333
254
|
|
|
334
255
|
## License
|
|
335
256
|
|
|
@@ -337,7 +258,7 @@ Licensed under either of
|
|
|
337
258
|
|
|
338
259
|
- Apache License, Version 2.0 ([LICENSE-APACHE2](LICENSE-APACHE2) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
339
260
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
|
340
|
-
|
|
261
|
+
|
|
341
262
|
at your option.
|
|
342
263
|
|
|
343
264
|
`SPDX-License-Identifier: Apache-2.0 OR MIT`
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
/** @ignore */
|
|
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
|
-
} & (SetAddPatch | SetRemovePatch |
|
|
7
|
+
} & (SetAddPatch | SetRemovePatch | RemovePatch | LiteralAddPatch);
|
|
8
|
+
/** @ignore */
|
|
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
|
+
/** @ignore */
|
|
18
21
|
export interface SetRemovePatch {
|
|
19
22
|
/** Mutation kind applied at the resolved `path`. */
|
|
20
23
|
op: "remove";
|
|
@@ -24,83 +27,50 @@ export interface SetRemovePatch {
|
|
|
24
27
|
* - A single primitive
|
|
25
28
|
* - An array of primitives
|
|
26
29
|
*/
|
|
27
|
-
value: number | string | boolean | (number | string | boolean)[];
|
|
28
|
-
}
|
|
29
|
-
export interface ObjectAddPatch {
|
|
30
|
-
/** Mutation kind applied at the resolved `path`. */
|
|
31
|
-
op: "add";
|
|
32
|
-
valType: "object";
|
|
30
|
+
value: number | string | boolean | object | (number | string | boolean | object)[];
|
|
33
31
|
}
|
|
32
|
+
/** @ignore */
|
|
34
33
|
export interface RemovePatch {
|
|
35
34
|
/** Mutation kind applied at the resolved `path`. */
|
|
36
35
|
op: "remove";
|
|
37
36
|
}
|
|
37
|
+
/** @ignore */
|
|
38
38
|
export interface LiteralAddPatch {
|
|
39
39
|
/** Mutation kind applied at the resolved `path`. */
|
|
40
40
|
op: "add";
|
|
41
41
|
/** The literal value to be added at the resolved `path` */
|
|
42
|
-
value: string | number | boolean;
|
|
42
|
+
value: string | number | boolean | object;
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
|
+
* @ignore
|
|
46
|
+
*
|
|
45
47
|
* Apply a diff to an object.
|
|
46
48
|
*
|
|
47
49
|
* The syntax is inspired by RFC 6902 but it is not compatible.
|
|
48
50
|
*
|
|
49
51
|
* It supports Sets for multi-valued properties:
|
|
50
52
|
* - Primitive values are added as Sets (Set<string | number | boolean>)
|
|
51
|
-
* - Multi-valued objects are stored in Sets, accessed by their
|
|
52
|
-
* - 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
|
|
53
55
|
*
|
|
54
56
|
* Path traversal:
|
|
55
|
-
* - 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
|
|
56
58
|
* - When traversing through a plain object, the path segment is a property name
|
|
57
59
|
*
|
|
58
|
-
* @example operations
|
|
59
|
-
* ```jsonc
|
|
60
|
-
* // === SINGLE OBJECT ===
|
|
61
|
-
* // Creating a single object (has @id at same level)
|
|
62
|
-
* { "op": "add", "path": "/urn:example:person1/address", "valType": "object" }
|
|
63
|
-
* { "op": "add", "path": "/urn:example:person1/address/@id", "value": "urn:test:address1" }
|
|
64
|
-
* // Adding primitives to single object
|
|
65
|
-
* { "op": "add", "path": "/urn:example:person1/address/street", "value": "1st street" }
|
|
66
|
-
* { "op": "add", "path": "/urn:example:person1/address/country", "value": "Greece" }
|
|
67
|
-
* // Remove a primitive from object
|
|
68
|
-
* { "op": "remove", "path": "/urn:example:person1/address/street" }
|
|
69
|
-
* // Remove the entire object
|
|
70
|
-
* { "op": "remove", "path": "/urn:example:person1/address" }
|
|
71
|
-
*
|
|
72
|
-
* // === MULTI-VALUED OBJECTS (Set) ===
|
|
73
|
-
* // Creating a multi-object container (NO @id at this level -> creates Set)
|
|
74
|
-
* { "op": "add", "path": "/urn:example:person1/children", "valType": "object" }
|
|
75
|
-
* // Adding an object to the Set (path includes object's @id)
|
|
76
|
-
* { "op": "add", "path": "/urn:example:person1/children/urn:example:child1", "valType": "object" }
|
|
77
|
-
* { "op": "add", "path": "/urn:example:person1/children/urn:example:child1/@id", "value": "urn:example:child1" }
|
|
78
|
-
* // Adding properties to object in Set
|
|
79
|
-
* { "op": "add", "path": "/urn:example:person1/children/urn:example:child1/name", "value": "Alice" }
|
|
80
|
-
* // Remove an object from Set
|
|
81
|
-
* { "op": "remove", "path": "/urn:example:person1/children/urn:example:child1" }
|
|
82
|
-
* // Remove all objects (the Set itself)
|
|
83
|
-
* { "op": "remove", "path": "/urn:example:person1/children" }
|
|
84
|
-
*
|
|
85
|
-
* // === PRIMITIVE SETS ===
|
|
86
|
-
* // Add primitive types to Sets
|
|
87
|
-
* { "op": "add", "valType": "set", "path": "/urn:example:person1/tags", "value": [1,2,3] }
|
|
88
|
-
* // Remove primitive types from a Set
|
|
89
|
-
* { "op": "remove", "valType": "set", "path": "/urn:example:person1/tags", "value": [1,2] }
|
|
90
|
-
* ```
|
|
91
|
-
*
|
|
92
60
|
* @param currentState The object before the patch
|
|
93
61
|
* @param patches An array of patches to apply to the object.
|
|
94
62
|
* @param ensurePathExists If true, create nested objects along the path if the path does not exist.
|
|
95
63
|
*
|
|
96
|
-
*
|
|
64
|
+
* Note: When creating new objects, this function pre-scans upcoming patches to find `@id` and `@graph`
|
|
97
65
|
* values that will be assigned to the object. This prevents the signal library's propGenerator
|
|
98
66
|
* from being triggered before these identity fields are set, which would cause it to generate
|
|
99
67
|
* random IDs unnecessarily.
|
|
100
68
|
*/
|
|
101
|
-
export declare function applyPatches(currentState: Record<string, any>, patches: Patch[], ensurePathExists?: boolean): void;
|
|
69
|
+
export declare function applyPatches(currentState: Record<string, any>, patches: Patch[], ormType: "set" | "discrete", ensurePathExists?: boolean): void;
|
|
102
70
|
/**
|
|
103
|
-
*
|
|
71
|
+
* @ignore
|
|
72
|
+
*
|
|
73
|
+
* See documentation for applyPatches
|
|
104
74
|
*/
|
|
105
|
-
export declare function applyPatchesToDeepSignal(currentState: object, patch: Patch[]): void;
|
|
75
|
+
export declare function applyPatchesToDeepSignal(currentState: object, patch: Patch[], ormType: "set" | "discrete"): void;
|
|
106
76
|
//# sourceMappingURL=applyPatches.d.ts.map
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"applyPatches.d.ts","sourceRoot":"","sources":["../../src/connector/applyPatches.ts"],"names":[],"mappings":"AAYA,cAAc;AACd,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,cAAc;AACd,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,cAAc;AACd,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,cAAc;AACd,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,EAAE,EAAE,QAAQ,CAAC;CAChB;AAED,cAAc;AACd,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;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;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"}
|