@interop/was-react 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -104,7 +104,7 @@ no per-collection key is derived, payloads are stored as-is locally and
104
104
  remotely, and the stored resource id is the payload's own logical `id`, so a
105
105
  public document keeps a stable, shareable resource URL across edits. Be aware
106
106
  that everything in a public payload is world-readable -- including the LWW
107
- bookkeeping fields `updatedAt` and `deviceId` (`deviceId` is a random
107
+ bookkeeping fields `updatedAt` and `clientId` (`clientId` is a random
108
108
  per-install identifier, but still a linkability handle across a user's public
109
109
  documents). Changing a collection's visibility after first use is a
110
110
  data-migration event, not a config tweak: rows written in the other mode stop
@@ -112,7 +112,7 @@ being readable. A registry that maps one WAS collection id to both visibilities
112
112
  is rejected at store open. Two payload constraints on public collections: a
113
113
  top-level object-valued `jwe` field is reserved (the read path uses it to
114
114
  recognize a stray encrypted envelope and refuses the row), and payloads should
115
- carry the `updatedAt` / `deviceId` LWW fields like any other collection --
115
+ carry the `updatedAt` / `clientId` LWW fields like any other collection --
116
116
  without them a concurrent multi-device edit falls back to server-wins.
117
117
 
118
118
  At login, a public collection is requested from the wallet with the distinct
@@ -138,7 +138,7 @@ export interface Note {
138
138
  body: string
139
139
  createdAt: string
140
140
  updatedAt: string
141
- deviceId: string
141
+ clientId: string
142
142
  }
143
143
 
144
144
  export const useNotes = createEntityStore<Note>('notes')
@@ -201,7 +201,7 @@ export function LoginPage() {
201
201
 
202
202
  ```tsx
203
203
  import { uuidv7 } from 'uuidv7'
204
- import { getDeviceId } from '@interop/was-react'
204
+ import { getClientId } from '@interop/was-react'
205
205
  import { useNotes } from './stores.js'
206
206
 
207
207
  export function Notes() {
@@ -216,7 +216,7 @@ export function Notes() {
216
216
  body: '',
217
217
  createdAt: now,
218
218
  updatedAt: now,
219
- deviceId: getDeviceId()
219
+ clientId: getClientId()
220
220
  })
221
221
  }
222
222
 
@@ -233,7 +233,7 @@ export function Notes() {
233
233
  }
234
234
  ```
235
235
 
236
- Entity payloads MUST carry `updatedAt` and `deviceId` (from `getDeviceId()`),
236
+ Entity payloads MUST carry `updatedAt` and `clientId` (from `getClientId()`),
237
237
  stamped on EVERY insert and update: they are the last-write-wins pair that
238
238
  settles concurrent multi-device edits of the same entity. A payload without them
239
239
  loses every sync conflict.
@@ -402,8 +402,8 @@ Hooks:
402
402
  zcaps). Pull is driven by the WAS `changes` feed; a low-frequency periodic
403
403
  re-sync (`sync.pollMs`, default 15s) keeps open sessions converging.
404
404
  - **Conflict resolution.** Last-writer-wins on the payload's own
405
- `(updatedAt, deviceId)` (ISO lexical compare, with a per-install random
406
- `deviceId` tiebreaker). Updates re-encrypt in place under the same envelope id
405
+ `(updatedAt, clientId)` (ISO lexical compare, with a per-install random
406
+ `clientId` tiebreaker). Updates re-encrypt in place under the same envelope id
407
407
  with `sequence`+1 (a mutable-head model); deletes are soft-delete tombstones.
408
408
  - **Status.** `useSyncStatus()` rolls the per-collection replication states up
409
409
  to an aggregate: `offline` (no replication running / local-only), or
@@ -419,7 +419,7 @@ cost.
419
419
  | ------------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------ |
420
420
  | `@interop/was-react` | Core: config, identity, auth, sync, storage, session store, React provider and hooks | `react`, `zustand`, `rxdb` |
421
421
  | `@interop/was-react/mui` | Optional `ProtectedRoute`, `ReconnectBanner`, `SyncStatusChip` | `@mui/material`, `@mui/icons-material`, `react-router` |
422
- | `@interop/was-react/dev` | Node-only `provisionDevGrants` + the `was-provision-dev-grants` CLI | (none; Node only) |
422
+ | `@interop/was-react/dev` | Node-only `provisionDevGrants` | (none; Node only) |
423
423
 
424
424
  MUI usage:
425
425
 
@@ -463,19 +463,6 @@ The `./dev` entry provisions a Space, collections, and delegated read/write
463
463
  grants against a running was-teaching-server, so an app can dev-sync without a
464
464
  CHAPI wallet in the loop. It is Node-only (uses `node:fs`).
465
465
 
466
- CLI:
467
-
468
- ```
469
- was-provision-dev-grants \
470
- --server-url http://localhost:3002 \
471
- --seed <hex-or-base64url 32-byte seed> \
472
- --collections notes \
473
- --space-name "Dev Space" \
474
- --out ./public/dev-grants.local.json
475
- ```
476
-
477
- Programmatic:
478
-
479
466
  ```ts
480
467
  import { provisionDevGrants } from '@interop/was-react/dev'
481
468
 
package/dist/index.d.ts CHANGED
@@ -25,7 +25,7 @@ export { loginWithWallet, requestGrants, LoginCancelledError, WalletUnsupportedE
25
25
  export type { WalletAPIMessage, IVPOffer, IVPRequest, IVPRDetails, IVPRQuery, IQueryByExample, IDIDAuthenticationQuery, IZcapQuery, IAppConnectQuery, IAppConnectCapabilityQuery, ICapabilityQueryDetail, WalletResponse, WalletRequestProfile, IVerifiableCredential, IVerifiablePresentation, IZcap } from './auth/walletRequestTypes.js';
26
26
  export * from './sync/index.js';
27
27
  export { LocalStore } from './storage/localStore.js';
28
- export { setLocalStore, requireStore, hasStore, clearLocalStore, setRemoteStore, requireRemoteStore, hasRemoteStore, clearRemoteStore, getDeviceId } from './storage/storageManager.js';
28
+ export { setLocalStore, requireStore, hasStore, clearLocalStore, setRemoteStore, requireRemoteStore, hasRemoteStore, clearRemoteStore, getClientId } from './storage/storageManager.js';
29
29
  export { createEntityStore, type EntityStore } from './storage/entityStore.js';
30
30
  export { WasRemoteStore, type MarkerResult, type EqualityQueryPage } from './storage/wasRemoteStore.js';
31
31
  export { publicUrlFor } from './storage/publicUrl.js';
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ export * from './sync/index.js';
32
32
  // stores, the delegated remote store, sync status, rehydrate mechanism, and the
33
33
  // replication controller.
34
34
  export { LocalStore } from './storage/localStore.js';
35
- export { setLocalStore, requireStore, hasStore, clearLocalStore, setRemoteStore, requireRemoteStore, hasRemoteStore, clearRemoteStore, getDeviceId } from './storage/storageManager.js';
35
+ export { setLocalStore, requireStore, hasStore, clearLocalStore, setRemoteStore, requireRemoteStore, hasRemoteStore, clearRemoteStore, getClientId } from './storage/storageManager.js';
36
36
  export { createEntityStore } from './storage/entityStore.js';
37
37
  export { WasRemoteStore } from './storage/wasRemoteStore.js';
38
38
  export { publicUrlFor } from './storage/publicUrl.js';
@@ -27,7 +27,7 @@ export interface DocumentApp<T extends object> {
27
27
  /**
28
28
  * The tier-1 document hook. Usable in any component below the provider.
29
29
  */
30
- useDocument: () => {
30
+ useAppDocument: () => {
31
31
  /**
32
32
  * The document: `undefined` during boot, then the stored value or the
33
33
  * configured `initial` when nothing has been written yet.
@@ -87,7 +87,7 @@ export interface DocumentApp<T extends object> {
87
87
  /**
88
88
  * Builds the complete wiring for a one-document app: a `WasAppConfig` with a
89
89
  * single sandbox collection and local-first onboarding, the singleton-document
90
- * store registry, and the typed `useDocument` hook bound to both.
90
+ * store registry, and the typed `useAppDocument` hook bound to both.
91
91
  *
92
92
  * @param options {object}
93
93
  * @param options.appName {string} human-readable name (consent reason lines)
@@ -1 +1 @@
1
- {"version":3,"file":"documentApp.d.ts","sourceRoot":"","sources":["../../src/react/documentApp.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,eAAe,EACf,aAAa,EACd,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAG5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAG/D;;GAEG;AACH,eAAO,MAAM,uBAAuB,aAAa,CAAA;AAOjD;;GAEG;AACH,eAAO,MAAM,sBAAsB,oBAAoB,CAAA;AAavD;;;GAGG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,MAAM;IAC3C;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAA;IACpB;;OAEG;IACH,QAAQ,EAAE,aAAa,CAAA;IACvB;;OAEG;IACH,WAAW,EAAE,MAAM;QACjB;;;WAGG;QACH,GAAG,EAAE,CAAC,GAAG,SAAS,CAAA;QAClB;;;;;WAKG;QACH,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QAC/D;;;WAGG;QACH,MAAM,EAAE,aAAa,CAAA;QACrB;;WAEG;QACH,IAAI,EAAE,UAAU,CAAA;QAChB;;WAEG;QACH,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;QAC/B;;;WAGG;QACH,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QACzC;;;;;;;WAOG;QACH,OAAO,EAAE,MAAM,OAAO,CAAC;YAAE,QAAQ,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAC,CAAA;QACpD;;;WAGG;QACH,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;QAC/B;;WAEG;QACH,UAAU,EAAE,OAAO,CAAA;QACnB;;WAEG;QACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KACrB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,EAClD,OAAO,EACP,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,MAAM,EACN,gBAAgB,EAChB,IAAI,EACJ,MAAM,EACP,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,CAAA;IAC9C,UAAU,EAAE,oBAAoB,CAAA;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,MAAM,CAAC,EAAE,eAAe,CAAA;CACzB,GAAG,WAAW,CAAC,CAAC,CAAC,CAgIjB"}
1
+ {"version":3,"file":"documentApp.d.ts","sourceRoot":"","sources":["../../src/react/documentApp.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,eAAe,EACf,aAAa,EACd,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAG5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAG/D;;GAEG;AACH,eAAO,MAAM,uBAAuB,aAAa,CAAA;AAOjD;;GAEG;AACH,eAAO,MAAM,sBAAsB,oBAAoB,CAAA;AAavD;;;GAGG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,MAAM;IAC3C;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAA;IACpB;;OAEG;IACH,QAAQ,EAAE,aAAa,CAAA;IACvB;;OAEG;IACH,cAAc,EAAE,MAAM;QACpB;;;WAGG;QACH,GAAG,EAAE,CAAC,GAAG,SAAS,CAAA;QAClB;;;;;WAKG;QACH,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QAC/D;;;WAGG;QACH,MAAM,EAAE,aAAa,CAAA;QACrB;;WAEG;QACH,IAAI,EAAE,UAAU,CAAA;QAChB;;WAEG;QACH,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;QAC/B;;;WAGG;QACH,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QACzC;;;;;;;WAOG;QACH,OAAO,EAAE,MAAM,OAAO,CAAC;YAAE,QAAQ,EAAE,OAAO,CAAA;SAAE,GAAG,IAAI,CAAC,CAAA;QACpD;;;WAGG;QACH,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;QAC/B;;WAEG;QACH,UAAU,EAAE,OAAO,CAAA;QACnB;;WAEG;QACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KACrB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,EAClD,OAAO,EACP,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,MAAM,EACN,gBAAgB,EAChB,IAAI,EACJ,MAAM,EACP,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,CAAA;IAC9C,UAAU,EAAE,oBAAoB,CAAA;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,MAAM,CAAC,EAAE,eAAe,CAAA;CACzB,GAAG,WAAW,CAAC,CAAC,CAAC,CAgIjB"}
@@ -5,16 +5,16 @@
5
5
  * The tier-1 "one sandbox document" facade: `defineDocumentApp` builds the
6
6
  * whole was-react wiring (config, store registry, entity store) for an app
7
7
  * whose entire model is a single key-value document -- an Excalidraw-style
8
- * editor, a browser-game save file -- and returns a typed `useDocument` hook
8
+ * editor, a browser-game save file -- and returns a typed `useAppDocument` hook
9
9
  * over it. The app never sees `createEntityStore`, grants parsing, or sync
10
10
  * internals: it renders `doc`, calls `update`, and optionally offers file
11
11
  * export/import and a "Save to Web Spaces" connect button.
12
12
  *
13
13
  * The facade is a degenerate entity store: one collection (the app-named
14
14
  * sandbox collection) holding one logical document under a fixed id. The
15
- * stored row wraps the app's data (`{ id, updatedAt, deviceId, data }`) so app
15
+ * stored row wraps the app's data (`{ id, updatedAt, clientId, data }`) so app
16
16
  * fields can never collide with the LWW fields the sync layer requires, and
17
- * the facade stamps `updatedAt`/`deviceId` on every write itself. Hydration
17
+ * the facade stamps `updatedAt`/`clientId` on every write itself. Hydration
18
18
  * goes through `LocalStore.hydrateSingleton`, which LWW-reconciles the
19
19
  * duplicate envelope rows two devices can mint for the same logical document.
20
20
  *
@@ -29,7 +29,7 @@
29
29
  */
30
30
  import { useStore } from 'zustand';
31
31
  import { createEntityStore } from '../storage/entityStore.js';
32
- import { getDeviceId, requireStore } from '../storage/storageManager.js';
32
+ import { getClientId, requireStore } from '../storage/storageManager.js';
33
33
  import { useAuthStore, useSession, useSyncStatus } from './hooks.js';
34
34
  /**
35
35
  * The localStore / RxDB collection key the facade's one collection uses.
@@ -46,7 +46,7 @@ export const DOCUMENT_EXPORT_FORMAT = 'was-document/v1';
46
46
  /**
47
47
  * Builds the complete wiring for a one-document app: a `WasAppConfig` with a
48
48
  * single sandbox collection and local-first onboarding, the singleton-document
49
- * store registry, and the typed `useDocument` hook bound to both.
49
+ * store registry, and the typed `useAppDocument` hook bound to both.
50
50
  *
51
51
  * @param options {object}
52
52
  * @param options.appName {string} human-readable name (consent reason lines)
@@ -103,7 +103,7 @@ export function defineDocumentApp({ appName, appOrigin, mediatorBase, document,
103
103
  await docStore.getState().upsert({
104
104
  id: DOCUMENT_ID,
105
105
  updatedAt: new Date().toISOString(),
106
- deviceId: getDeviceId({
106
+ clientId: getClientId({
107
107
  ...(storageKeyPrefix !== undefined && { storageKeyPrefix })
108
108
  }),
109
109
  data
@@ -152,7 +152,7 @@ export function defineDocumentApp({ appName, appOrigin, mediatorBase, document,
152
152
  const imported = record.document;
153
153
  await update(() => imported);
154
154
  }
155
- function useDocument() {
155
+ function useAppDocument() {
156
156
  const held = docStore(state => state.byId.get(DOCUMENT_ID));
157
157
  const { status, authenticating, error } = useSession();
158
158
  const { state: syncState } = useSyncStatus();
@@ -175,6 +175,6 @@ export function defineDocumentApp({ appName, appOrigin, mediatorBase, document,
175
175
  error
176
176
  };
177
177
  }
178
- return { config, registry, useDocument };
178
+ return { config, registry, useAppDocument };
179
179
  }
180
180
  //# sourceMappingURL=documentApp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"documentApp.js","sourceRoot":"","sources":["../../src/react/documentApp.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"documentApp.js","sourceRoot":"","sources":["../../src/react/documentApp.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AASlC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAExE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEpE;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAAA;AAEjD;;GAEG;AACH,MAAM,WAAW,GAAG,MAAM,CAAA;AAE1B;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,iBAAiB,CAAA;AAsFvD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAAmB,EAClD,OAAO,EACP,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,MAAM,EACN,gBAAgB,EAChB,IAAI,EACJ,MAAM,EAWP;IACC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAA;IAC1C,MAAM,QAAQ,GAAG,iBAAiB,CAAoB,uBAAuB,CAAC,CAAA;IAE9E,MAAM,MAAM,GAAiB;QAC3B,OAAO;QACP,SAAS;QACT,GAAG,CAAC,YAAY,KAAK,SAAS,IAAI,EAAE,YAAY,EAAE,CAAC;QACnD,WAAW,EAAE,CAAC,EAAE,GAAG,EAAE,uBAAuB,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC;QACjE,UAAU,EAAE,aAAa;QACzB,UAAU;QACV,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;QACvC,GAAG,CAAC,gBAAgB,KAAK,SAAS,IAAI,EAAE,gBAAgB,EAAE,CAAC;QAC3D,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,CAAC;QACnC,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;KACxC,CAAA;IAED,MAAM,QAAQ,GAAkB;QAC9B,CAAC,uBAAuB,CAAC,EAAE;YACzB,8DAA8D;YAC9D,yDAAyD;YACzD,OAAO,EAAE,KAAK,IAAI,EAAE;gBAClB,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE,CAAC,gBAAgB,CAClD,uBAAuB,CACxB,CAAA;gBACD,QAAQ,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YACxD,CAAC;YACD,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAwB,CAAC;YAClE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;YAC5C,KAAK,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;SAChD;KACF,CAAA;IAED;;;;;;OAMG;IACH,KAAK,UAAU,MAAM,CAAC,KAAoC;QACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACtD,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,OAAO,CAAA;QAClC,MAAM,IAAI,GACR,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,EAAE,CAAA;QACnE,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC;YAC/B,EAAE,EAAE,WAAW;YACf,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE,WAAW,CAAC;gBACpB,GAAG,CAAC,gBAAgB,KAAK,SAAS,IAAI,EAAE,gBAAgB,EAAE,CAAC;aAC5D,CAAC;YACF,IAAI;SACL,CAAC,CAAA;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,UAAU,UAAU;QACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACtD,MAAM,IAAI,GAAG;YACX,MAAM,EAAE,sBAAsB;YAC9B,GAAG,EAAE,OAAO;YACZ,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACpC,QAAQ,EAAE,IAAI,EAAE,IAAI,IAAI,OAAO;SAChC,CAAA;QACD,OAAO,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;YAC/C,IAAI,EAAE,kBAAkB;SACzB,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;OAMG;IACH,KAAK,UAAU,UAAU,CAAC,IAAU;QAClC,IAAI,MAAe,CAAA;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;QAC/D,CAAC;QACD,MAAM,MAAM,GAAG,MAAkD,CAAA;QACjE,IACE,MAAM,KAAK,IAAI;YACf,MAAM,CAAC,MAAM,KAAK,sBAAsB;YACxC,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;YACnC,MAAM,CAAC,QAAQ,KAAK,IAAI,EACxB,CAAC;YACD,MAAM,IAAI,KAAK,CACb,UAAU,sBAAsB,kCAAkC,CACnE,CAAA;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAA;QAChC,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAA;IAC9B,CAAC;IAED,SAAS,cAAc;QACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAA;QAC3D,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CAAA;QACtD,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,aAAa,EAAE,CAAA;QAC5C,MAAM,KAAK,GAAG,YAAY,EAAE,CAAA;QAC5B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACnD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QACrD,0EAA0E;QAC1E,+DAA+D;QAC/D,MAAM,GAAG,GAAG,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,OAAO,CAAC,CAAA;QACnE,OAAO;YACL,GAAG;YACH,MAAM;YACN,MAAM;YACN,IAAI,EAAE,SAAS;YACf,UAAU;YACV,UAAU;YACV,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE;YACtB,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE;YAC1B,UAAU,EAAE,cAAc;YAC1B,KAAK;SACN,CAAA;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAA;AAC7C,CAAC"}
@@ -14,30 +14,30 @@
14
14
  * the same last-write-wins rule replication runs ({@link remotePayloadWins});
15
15
  * a connected doc without LWW fields always loses to a stamped adopted one.
16
16
  *
17
- * Adopted payloads missing `updatedAt`/`deviceId` are stamped at adoption time
17
+ * Adopted payloads missing `updatedAt`/`clientId` are stamped at adoption time
18
18
  * (the sync layer's conflict resolution requires them); payloads that already
19
19
  * carry them keep their original values, so a doc edited long ago does not
20
20
  * suddenly outrank fresher remote edits.
21
21
  */
22
22
  import { uuidv7 } from 'uuidv7';
23
23
  import { lwwFields, remotePayloadWins } from '../sync/lww.js';
24
- import { getDeviceId } from './storageManager.js';
24
+ import { getClientId } from './storageManager.js';
25
25
  /**
26
26
  * The one-per-merge stamp applied to payloads missing their LWW fields.
27
27
  *
28
- * @returns {{ updatedAt: string, deviceId: string }}
28
+ * @returns {{ updatedAt: string, clientId: string }}
29
29
  */
30
30
  function adoptionStamp() {
31
- let deviceId;
31
+ let clientId;
32
32
  try {
33
- deviceId = getDeviceId();
33
+ clientId = getClientId();
34
34
  }
35
35
  catch {
36
36
  // No localStorage (non-browser environments): an unpersisted id still
37
37
  // gives the LWW tiebreak a deterministic value for this merge.
38
- deviceId = uuidv7();
38
+ clientId = uuidv7();
39
39
  }
40
- return { updatedAt: new Date().toISOString(), deviceId };
40
+ return { updatedAt: new Date().toISOString(), clientId };
41
41
  }
42
42
  /**
43
43
  * Merges the collected anonymous-replica payloads into `store` (the already
@@ -60,7 +60,7 @@ export interface EntityStore<T extends {
60
60
  * stream owns the persisted row already). Used for per-doc reactive patching
61
61
  * of pulled/conflict-resolved remote changes; coalesced into one store update
62
62
  * per pull burst (see the reactivity note above). When both the incoming and
63
- * the held doc carry the LWW fields (`updatedAt` + `deviceId`), a stale
63
+ * the held doc carry the LWW fields (`updatedAt` + `clientId`), a stale
64
64
  * incoming doc is discarded rather than clobbering the newer held one.
65
65
  */
66
66
  patch: (doc: T) => void;
@@ -152,7 +152,7 @@ export declare class LocalStore {
152
152
  * created the singleton before syncing produce distinct envelope rows that all
153
153
  * decrypt to the same logical id; because LWW conflict resolution is
154
154
  * per-envelope-id, those duplicates never reconcile on their own. This keeps
155
- * the last-writer-wins winner (payload `updatedAt`, `deviceId` tiebreak) and
155
+ * the last-writer-wins winner (payload `updatedAt`, `clientId` tiebreak) and
156
156
  * tombstones the losers so the deletion replicates and the space converges on
157
157
  * one row. Returns the winning payload, or `null` when the collection is empty.
158
158
  *
@@ -162,7 +162,7 @@ export declare class LocalStore {
162
162
  hydrateSingleton<T extends {
163
163
  id: string;
164
164
  updatedAt: string;
165
- deviceId: string;
165
+ clientId: string;
166
166
  }>(key: string): Promise<T | null>;
167
167
  /**
168
168
  * Decrypts a single EDV envelope into its plaintext payload, for per-doc
@@ -111,7 +111,7 @@ export class LocalStore {
111
111
  // Each collection gets an LWW conflict handler bound to its own cipher, so
112
112
  // a 412 push conflict (concurrent multi-device edit of the same mutable
113
113
  // head) is settled by decrypting both sides and comparing payload
114
- // `updatedAt` (deviceId tiebreak) rather than RxDB's default master-wins.
114
+ // `updatedAt` (clientId tiebreak) rather than RxDB's default master-wins.
115
115
  // On a public collection the codec is pass-through, so the handler reads
116
116
  // those fields directly off the plaintext payload.
117
117
  const collectionsConfig = Object.fromEntries(collections.map(({ key }) => {
@@ -351,7 +351,7 @@ export class LocalStore {
351
351
  * created the singleton before syncing produce distinct envelope rows that all
352
352
  * decrypt to the same logical id; because LWW conflict resolution is
353
353
  * per-envelope-id, those duplicates never reconcile on their own. This keeps
354
- * the last-writer-wins winner (payload `updatedAt`, `deviceId` tiebreak) and
354
+ * the last-writer-wins winner (payload `updatedAt`, `clientId` tiebreak) and
355
355
  * tombstones the losers so the deletion replicates and the space converges on
356
356
  * one row. Returns the winning payload, or `null` when the collection is empty.
357
357
  *
@@ -53,15 +53,15 @@ export declare function hasRemoteStore(): boolean;
53
53
  */
54
54
  export declare function clearRemoteStore(): void;
55
55
  /**
56
- * A stable per-install device id (the last-write-wins tiebreak stamped into
57
- * every payload), persisted in localStorage under `<prefix>deviceId`.
56
+ * A stable per-install client id (the last-write-wins tiebreak stamped into
57
+ * every payload), persisted in localStorage under `<prefix>clientId`.
58
58
  *
59
59
  * @param [options] {object}
60
60
  * @param [options.storageKeyPrefix] {string} the localStorage key prefix
61
61
  * (defaults to {@link DEFAULT_STORAGE_KEY_PREFIX})
62
62
  * @returns {string}
63
63
  */
64
- export declare function getDeviceId({ storageKeyPrefix }?: {
64
+ export declare function getClientId({ storageKeyPrefix }?: {
65
65
  storageKeyPrefix?: string;
66
66
  }): string;
67
67
  //# sourceMappingURL=storageManager.d.ts.map
@@ -3,7 +3,7 @@
3
3
  */
4
4
  /**
5
5
  * The storage manager: a thin process-wide holder for the one {@link LocalStore}
6
- * instance, the per-session {@link WasRemoteStore}, plus the per-install device
6
+ * instance, the per-session {@link WasRemoteStore}, plus the per-install client
7
7
  * id. Entity stores reach for the stores through {@link requireStore} /
8
8
  * {@link requireRemoteStore} inside their verbs rather than importing them
9
9
  * directly, which keeps this module free of store imports (no cycle) and lets
@@ -88,20 +88,20 @@ export function clearRemoteStore() {
88
88
  remoteStore = null;
89
89
  }
90
90
  /**
91
- * A stable per-install device id (the last-write-wins tiebreak stamped into
92
- * every payload), persisted in localStorage under `<prefix>deviceId`.
91
+ * A stable per-install client id (the last-write-wins tiebreak stamped into
92
+ * every payload), persisted in localStorage under `<prefix>clientId`.
93
93
  *
94
94
  * @param [options] {object}
95
95
  * @param [options.storageKeyPrefix] {string} the localStorage key prefix
96
96
  * (defaults to {@link DEFAULT_STORAGE_KEY_PREFIX})
97
97
  * @returns {string}
98
98
  */
99
- export function getDeviceId({ storageKeyPrefix = DEFAULT_STORAGE_KEY_PREFIX } = {}) {
100
- const deviceIdKey = `${storageKeyPrefix}deviceId`;
101
- let id = localStorage.getItem(deviceIdKey);
99
+ export function getClientId({ storageKeyPrefix = DEFAULT_STORAGE_KEY_PREFIX } = {}) {
100
+ const clientIdKey = `${storageKeyPrefix}clientId`;
101
+ let id = localStorage.getItem(clientIdKey);
102
102
  if (!id) {
103
103
  id = uuidv7();
104
- localStorage.setItem(deviceIdKey, id);
104
+ localStorage.setItem(clientIdKey, id);
105
105
  }
106
106
  return id;
107
107
  }
@@ -7,35 +7,35 @@
7
7
  * two payloads and converge on the same winner with no coordination.
8
8
  *
9
9
  * `updatedAt` (an ISO-8601 string, so lexical compare == chronological compare)
10
- * decides; `deviceId` breaks an exact tie deterministically. Both fields live in
10
+ * decides; `clientId` breaks an exact tie deterministically. Both fields live in
11
11
  * the payload (never the envelope-level checkpoint `updatedAt`).
12
12
  */
13
13
  /**
14
14
  * Whether the remote payload wins over the local one under last-write-wins.
15
15
  * Later `updatedAt` wins; on an exact `updatedAt` tie the lexically greater
16
- * `deviceId` wins (an arbitrary but deterministic, replica-independent choice).
16
+ * `clientId` wins (an arbitrary but deterministic, replica-independent choice).
17
17
  *
18
- * @param remote {{ updatedAt: string; deviceId: string }}
19
- * @param local {{ updatedAt: string; deviceId: string }}
18
+ * @param remote {{ updatedAt: string; clientId: string }}
19
+ * @param local {{ updatedAt: string; clientId: string }}
20
20
  * @returns {boolean} true if the remote payload should replace the local one
21
21
  */
22
22
  export declare function remotePayloadWins(remote: {
23
23
  updatedAt: string;
24
- deviceId: string;
24
+ clientId: string;
25
25
  }, local: {
26
26
  updatedAt: string;
27
- deviceId: string;
27
+ clientId: string;
28
28
  }): boolean;
29
29
  /**
30
30
  * Reads the LWW fields off a doc when it carries them. Storage payloads are
31
- * generic over `{ id: string }`, so docs without `updatedAt`/`deviceId` are
31
+ * generic over `{ id: string }`, so docs without `updatedAt`/`clientId` are
32
32
  * legal; callers fall back to their own rule for those.
33
33
  *
34
34
  * @param doc {unknown}
35
- * @returns {{ updatedAt: string, deviceId: string } | null}
35
+ * @returns {{ updatedAt: string, clientId: string } | null}
36
36
  */
37
37
  export declare function lwwFields(doc: unknown): {
38
38
  updatedAt: string;
39
- deviceId: string;
39
+ clientId: string;
40
40
  } | null;
41
41
  //# sourceMappingURL=lww.d.ts.map
package/dist/sync/lww.js CHANGED
@@ -7,36 +7,36 @@
7
7
  * two payloads and converge on the same winner with no coordination.
8
8
  *
9
9
  * `updatedAt` (an ISO-8601 string, so lexical compare == chronological compare)
10
- * decides; `deviceId` breaks an exact tie deterministically. Both fields live in
10
+ * decides; `clientId` breaks an exact tie deterministically. Both fields live in
11
11
  * the payload (never the envelope-level checkpoint `updatedAt`).
12
12
  */
13
13
  /**
14
14
  * Whether the remote payload wins over the local one under last-write-wins.
15
15
  * Later `updatedAt` wins; on an exact `updatedAt` tie the lexically greater
16
- * `deviceId` wins (an arbitrary but deterministic, replica-independent choice).
16
+ * `clientId` wins (an arbitrary but deterministic, replica-independent choice).
17
17
  *
18
- * @param remote {{ updatedAt: string; deviceId: string }}
19
- * @param local {{ updatedAt: string; deviceId: string }}
18
+ * @param remote {{ updatedAt: string; clientId: string }}
19
+ * @param local {{ updatedAt: string; clientId: string }}
20
20
  * @returns {boolean} true if the remote payload should replace the local one
21
21
  */
22
22
  export function remotePayloadWins(remote, local) {
23
23
  if (remote.updatedAt !== local.updatedAt) {
24
24
  return remote.updatedAt > local.updatedAt;
25
25
  }
26
- return remote.deviceId > local.deviceId;
26
+ return remote.clientId > local.clientId;
27
27
  }
28
28
  /**
29
29
  * Reads the LWW fields off a doc when it carries them. Storage payloads are
30
- * generic over `{ id: string }`, so docs without `updatedAt`/`deviceId` are
30
+ * generic over `{ id: string }`, so docs without `updatedAt`/`clientId` are
31
31
  * legal; callers fall back to their own rule for those.
32
32
  *
33
33
  * @param doc {unknown}
34
- * @returns {{ updatedAt: string, deviceId: string } | null}
34
+ * @returns {{ updatedAt: string, clientId: string } | null}
35
35
  */
36
36
  export function lwwFields(doc) {
37
- const { updatedAt, deviceId } = doc;
38
- return typeof updatedAt === 'string' && typeof deviceId === 'string'
39
- ? { updatedAt, deviceId }
37
+ const { updatedAt, clientId } = doc;
38
+ return typeof updatedAt === 'string' && typeof clientId === 'string'
39
+ ? { updatedAt, clientId }
40
40
  : null;
41
41
  }
42
42
  //# sourceMappingURL=lww.js.map
@@ -7,17 +7,17 @@
7
7
  * correct for content-addressed (immutable-per-id) collections but wrong here:
8
8
  * every entity is a mutable head that two devices can edit concurrently, so a
9
9
  * genuine content conflict must be settled by last-write-wins on the payload's
10
- * own `updatedAt` (deviceId tiebreak) -- exactly the rule two offline replicas
10
+ * own `updatedAt` (clientId tiebreak) -- exactly the rule two offline replicas
11
11
  * apply independently to converge.
12
12
  *
13
13
  * The wrinkle is that the conflicting bodies are EDV envelopes (ciphertext), so
14
14
  * `resolve` must decrypt both sides through this collection's cipher before it
15
- * can compare the plaintext `updatedAt` / `deviceId`. `isEqual` stays cheap and
15
+ * can compare the plaintext `updatedAt` / `clientId`. `isEqual` stays cheap and
16
16
  * synchronous (a structural compare of the opaque bodies), as RxDB requires.
17
17
  *
18
18
  * Convergence: the server holds ONE winner of the push race as `realMasterState`;
19
19
  * every replica compares that same master against its own local edit, and the
20
- * `payloadWins` comparator is a total order over `(updatedAt, deviceId)`, so the
20
+ * `payloadWins` comparator is a total order over `(updatedAt, clientId)`, so the
21
21
  * globally-latest payload wins on every replica with no coordination.
22
22
  *
23
23
  * The resolution rules, in order:
@@ -57,7 +57,7 @@ import type { Json, SyncedDoc } from './types.js';
57
57
  */
58
58
  interface LwwPayload {
59
59
  updatedAt: string;
60
- deviceId: string;
60
+ clientId: string;
61
61
  }
62
62
  /**
63
63
  * Builds an RxDB conflict handler that settles content conflicts by payload LWW,
@@ -67,7 +67,7 @@ interface LwwPayload {
67
67
  * @param [payloadWins] {(remote: LwwPayload, local: LwwPayload) => boolean}
68
68
  * the total-order comparator deciding whether the remote payload replaces the
69
69
  * local one; defaults to {@link remotePayloadWins} (later `updatedAt` wins,
70
- * `deviceId` breaks a tie)
70
+ * `clientId` breaks a tie)
71
71
  * @returns {import('rxdb/plugins/core').RxConflictHandler<SyncedDoc>}
72
72
  */
73
73
  export declare function makeLwwConflictHandler(decrypt: (envelope: Json) => Promise<Json>, payloadWins?: (remote: LwwPayload, local: LwwPayload) => boolean): {
@@ -6,7 +6,7 @@ function bodiesEqual(a, b) {
6
6
  return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
7
7
  }
8
8
  /**
9
- * Decrypts one side's envelope into its `{ updatedAt, deviceId }`, or `null`
9
+ * Decrypts one side's envelope into its `{ updatedAt, clientId }`, or `null`
10
10
  * when the side has no decryptable payload (a tombstone, or an absent/corrupt
11
11
  * body). `null` means "no comparable timestamp", handled by the caller.
12
12
  */
@@ -17,8 +17,8 @@ async function lwwFieldsOf(doc, decrypt) {
17
17
  try {
18
18
  const payload = (await decrypt(doc.data));
19
19
  if (typeof payload.updatedAt === 'string' &&
20
- typeof payload.deviceId === 'string') {
21
- return { updatedAt: payload.updatedAt, deviceId: payload.deviceId };
20
+ typeof payload.clientId === 'string') {
21
+ return { updatedAt: payload.updatedAt, clientId: payload.clientId };
22
22
  }
23
23
  return null;
24
24
  }
@@ -34,7 +34,7 @@ async function lwwFieldsOf(doc, decrypt) {
34
34
  * @param [payloadWins] {(remote: LwwPayload, local: LwwPayload) => boolean}
35
35
  * the total-order comparator deciding whether the remote payload replaces the
36
36
  * local one; defaults to {@link remotePayloadWins} (later `updatedAt` wins,
37
- * `deviceId` breaks a tie)
37
+ * `clientId` breaks a tie)
38
38
  * @returns {import('rxdb/plugins/core').RxConflictHandler<SyncedDoc>}
39
39
  */
40
40
  export function makeLwwConflictHandler(decrypt, payloadWins = remotePayloadWins) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@interop/was-react",
3
3
  "description": "React library for building 'Bring Your Own Everything' (BYOE) apps on Wallet Attached Storage: DID Auth login via CHAPI wallet, local-first encrypted storage, and background sync to a WAS server.",
4
- "version": "0.2.1",
4
+ "version": "0.3.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "pnpm run clear && tsc",
@@ -18,9 +18,6 @@
18
18
  "test:node": "vitest run",
19
19
  "test:coverage": "vitest run --coverage"
20
20
  },
21
- "bin": {
22
- "was-provision-dev-grants": "./dist/dev/cli.js"
23
- },
24
21
  "files": [
25
22
  "dist",
26
23
  "README.md",
package/dist/dev/cli.d.ts DELETED
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Decodes a seed string as hex (all hex chars, even length) or base64url.
4
- *
5
- * @param input {string}
6
- * @returns {Uint8Array}
7
- */
8
- export declare function parseSeed(input: string): Uint8Array;
9
- /**
10
- * Splits a comma-separated collection-ids string into a trimmed, non-empty list.
11
- *
12
- * @param input {string}
13
- * @returns {string[]}
14
- */
15
- export declare function parseCollections(input: string): string[];
16
- //# sourceMappingURL=cli.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/dev/cli.ts"],"names":[],"mappings":";AAqCA;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAOnD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAKxD"}
package/dist/dev/cli.js DELETED
@@ -1,119 +0,0 @@
1
- #!/usr/bin/env node
2
- /*!
3
- * Copyright (c) 2026 Interop Alliance. All rights reserved.
4
- */
5
- /**
6
- * CLI wrapper around `provisionDevGrants`. Creates a dev Space + collections on a
7
- * running was-teaching-server and delegates per-collection RW zcaps to the app
8
- * DID derived from `--seed`, optionally writing the grants JSON to `--out`.
9
- *
10
- * Usage:
11
- * was-provision-dev-grants \
12
- * --server-url http://localhost:3002 \
13
- * --seed <hex-or-base64url 32-byte seed> \
14
- * --collections action-items,projects,goals \
15
- * [--space-name "Dev Space"] [--out ./public/dev-grants.local.json] [--probe]
16
- */
17
- import { parseArgs } from 'node:util';
18
- import { argv } from 'node:process';
19
- import { pathToFileURL } from 'node:url';
20
- import { base64urlnopad, hex } from '@scure/base';
21
- import { provisionDevGrants } from './provisionDevGrants.js';
22
- const USAGE = `Usage: was-provision-dev-grants [options]
23
-
24
- Required:
25
- --server-url <url> base URL of a running was-teaching-server
26
- --seed <string> the app master seed, hex or base64url encoded
27
- --collections <ids> comma-separated WAS collection ids
28
-
29
- Optional:
30
- --space-name <name> Space name (default "Dev Space")
31
- --out <path> write the { grants } JSON to this file
32
- --probe probe whether the delegated RW zcap authorizes the
33
- encryption-marker PUT
34
- --help show this message
35
- `;
36
- /**
37
- * Decodes a seed string as hex (all hex chars, even length) or base64url.
38
- *
39
- * @param input {string}
40
- * @returns {Uint8Array}
41
- */
42
- export function parseSeed(input) {
43
- const value = input.trim();
44
- if (/^[0-9a-fA-F]+$/.test(value) && value.length % 2 === 0) {
45
- // hex.decode requires lowercase, even-length input.
46
- return hex.decode(value.toLowerCase());
47
- }
48
- return base64urlnopad.decode(value);
49
- }
50
- /**
51
- * Splits a comma-separated collection-ids string into a trimmed, non-empty list.
52
- *
53
- * @param input {string}
54
- * @returns {string[]}
55
- */
56
- export function parseCollections(input) {
57
- return input
58
- .split(',')
59
- .map(id => id.trim())
60
- .filter(id => id.length > 0);
61
- }
62
- async function main() {
63
- const { values } = parseArgs({
64
- options: {
65
- 'server-url': { type: 'string' },
66
- seed: { type: 'string' },
67
- collections: { type: 'string' },
68
- 'space-name': { type: 'string' },
69
- out: { type: 'string' },
70
- probe: { type: 'boolean', default: false },
71
- help: { type: 'boolean', default: false }
72
- }
73
- });
74
- if (values.help) {
75
- console.log(USAGE);
76
- return;
77
- }
78
- const serverUrl = values['server-url'];
79
- const seedInput = values.seed;
80
- const collectionsInput = values.collections;
81
- const missing = [];
82
- if (!serverUrl) {
83
- missing.push('--server-url');
84
- }
85
- if (!seedInput) {
86
- missing.push('--seed');
87
- }
88
- if (!collectionsInput) {
89
- missing.push('--collections');
90
- }
91
- if (missing.length > 0) {
92
- console.error(`Missing required argument(s): ${missing.join(', ')}\n`);
93
- console.error(USAGE);
94
- process.exit(1);
95
- }
96
- const collections = parseCollections(collectionsInput);
97
- if (collections.length === 0) {
98
- console.error('--collections must list at least one collection id.\n');
99
- console.error(USAGE);
100
- process.exit(1);
101
- }
102
- await provisionDevGrants({
103
- serverUrl: serverUrl,
104
- seed: parseSeed(seedInput),
105
- collections,
106
- spaceName: values['space-name'],
107
- outFile: values.out,
108
- probe: values.probe,
109
- log: console.log
110
- });
111
- }
112
- // Run only when invoked as a script, not when imported (e.g. by tests).
113
- if (argv[1] !== undefined && import.meta.url === pathToFileURL(argv[1]).href) {
114
- main().catch(err => {
115
- console.error('Provisioning failed:', err);
116
- process.exit(1);
117
- });
118
- }
119
- //# sourceMappingURL=cli.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/dev/cli.ts"],"names":[],"mappings":";AACA;;GAEG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D,MAAM,KAAK,GAAG;;;;;;;;;;;;;CAab,CAAA;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC1B,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3D,oDAAoD;QACpD,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;IACxC,CAAC;IACD,OAAO,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;AAChC,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC3B,OAAO,EAAE;YACP,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAChC,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC/B,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAChC,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACvB,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YAC1C,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;SAC1C;KACF,CAAC,CAAA;IAEF,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAClB,OAAM;IACR,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,CAAA;IACtC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAA;IAC7B,MAAM,gBAAgB,GAAG,MAAM,CAAC,WAAW,CAAA;IAC3C,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IAC9B,CAAC;IACD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACxB,CAAC;IACD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;IAC/B,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,iCAAiC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,WAAW,GAAG,gBAAgB,CAAC,gBAAiB,CAAC,CAAA;IACvD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAA;QACtE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,kBAAkB,CAAC;QACvB,SAAS,EAAE,SAAU;QACrB,IAAI,EAAE,SAAS,CAAC,SAAU,CAAC;QAC3B,WAAW;QACX,SAAS,EAAE,MAAM,CAAC,YAAY,CAAC;QAC/B,OAAO,EAAE,MAAM,CAAC,GAAG;QACnB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,GAAG,EAAE,OAAO,CAAC,GAAG;KACjB,CAAC,CAAA;AACJ,CAAC;AAED,wEAAwE;AACxE,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7E,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;QACjB,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAA;QAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAC,CAAA;AACJ,CAAC"}