@interop/was-react 0.3.6 → 0.5.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.
Files changed (65) hide show
  1. package/README.md +84 -11
  2. package/dist/auth/loginFlow.d.ts +6 -0
  3. package/dist/auth/loginFlow.d.ts.map +1 -1
  4. package/dist/auth/loginFlow.js +28 -9
  5. package/dist/auth/loginFlow.js.map +1 -1
  6. package/dist/auth/loginRequest.d.ts +22 -1
  7. package/dist/auth/loginRequest.d.ts.map +1 -1
  8. package/dist/auth/loginRequest.js +17 -1
  9. package/dist/auth/loginRequest.js.map +1 -1
  10. package/dist/config.d.ts +51 -0
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +0 -0
  13. package/dist/config.js.map +1 -1
  14. package/dist/identity/agents.d.ts +38 -11
  15. package/dist/identity/agents.d.ts.map +1 -1
  16. package/dist/identity/agents.js +35 -11
  17. package/dist/identity/agents.js.map +1 -1
  18. package/dist/identity/initAppSession.d.ts +6 -6
  19. package/dist/identity/initAppSession.js +6 -6
  20. package/dist/identity/seedCredential.d.ts +29 -5
  21. package/dist/identity/seedCredential.d.ts.map +1 -1
  22. package/dist/identity/seedCredential.js +47 -11
  23. package/dist/identity/seedCredential.js.map +1 -1
  24. package/dist/identity/seedStore.d.ts +11 -1
  25. package/dist/identity/seedStore.d.ts.map +1 -1
  26. package/dist/identity/seedStore.js +9 -0
  27. package/dist/identity/seedStore.js.map +1 -1
  28. package/dist/index.d.ts +7 -6
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +8 -6
  31. package/dist/index.js.map +1 -1
  32. package/dist/react/hooks.d.ts +12 -0
  33. package/dist/react/hooks.d.ts.map +1 -1
  34. package/dist/react/hooks.js +14 -0
  35. package/dist/react/hooks.js.map +1 -1
  36. package/dist/react/index.d.ts +1 -1
  37. package/dist/react/index.d.ts.map +1 -1
  38. package/dist/react/index.js +1 -1
  39. package/dist/react/index.js.map +1 -1
  40. package/dist/session/authStore.d.ts +9 -0
  41. package/dist/session/authStore.d.ts.map +1 -1
  42. package/dist/session/authStore.js +73 -18
  43. package/dist/session/authStore.js.map +1 -1
  44. package/dist/storage/adopt.js +2 -2
  45. package/dist/storage/localStore.d.ts +73 -11
  46. package/dist/storage/localStore.d.ts.map +1 -1
  47. package/dist/storage/localStore.js +187 -30
  48. package/dist/storage/localStore.js.map +1 -1
  49. package/dist/storage/sharedCollectionReader.d.ts +95 -0
  50. package/dist/storage/sharedCollectionReader.d.ts.map +1 -0
  51. package/dist/storage/sharedCollectionReader.js +338 -0
  52. package/dist/storage/sharedCollectionReader.js.map +1 -0
  53. package/dist/storage/wasRemoteStore.d.ts +24 -2
  54. package/dist/storage/wasRemoteStore.d.ts.map +1 -1
  55. package/dist/storage/wasRemoteStore.js +40 -0
  56. package/dist/storage/wasRemoteStore.js.map +1 -1
  57. package/dist/storage/wasSync.d.ts +42 -5
  58. package/dist/storage/wasSync.d.ts.map +1 -1
  59. package/dist/storage/wasSync.js +71 -5
  60. package/dist/storage/wasSync.js.map +1 -1
  61. package/dist/sync/docCipher.d.ts +19 -3
  62. package/dist/sync/docCipher.d.ts.map +1 -1
  63. package/dist/sync/docCipher.js +24 -59
  64. package/dist/sync/docCipher.js.map +1 -1
  65. package/package.json +5 -5
@@ -1,6 +1,8 @@
1
1
  import { WasRemoteStore } from './wasRemoteStore.js';
2
+ import { SharedCollectionReader } from './sharedCollectionReader.js';
2
3
  /**
3
- * Builds the remote store and starts background replication.
4
+ * Builds the remote store, opens the shared-collection readers, and starts
5
+ * background replication.
4
6
  *
5
7
  * @param options {object}
6
8
  * @param options.parsed {ParsedGrants}
@@ -11,11 +13,21 @@ import { WasRemoteStore } from './wasRemoteStore.js';
11
13
  * @param options.syncController {SyncController} a fresh per-session controller
12
14
  * @param options.onRemoteChange {(collectionKey, event) => void} per-doc
13
15
  * reactive patcher for pulled/conflict-resolved remote changes
16
+ * @param [options.sharedCollections] {SharedCollectionConfig[]} the shared
17
+ * (read-only, wallet-owned) registry; never replicated, never written to
18
+ * @param [options.identityKeys] {object} this app's IDENTITY key-agreement key
19
+ * and its resolver, the recipient identity a wallet writes into a shared
20
+ * collection's epoch roster. Required to open any shared-collection reader
21
+ * @param [options.identityKeys.keyAgreementKey] {IKeyAgreementKey}
22
+ * @param [options.identityKeys.keyResolver] {IKeyResolver}
14
23
  * @param [options.onAuthError] {() => void} fired when replication hits a
15
24
  * 401/403 (expired/revoked access) -- wired to the reconnect banner
16
- * @returns {Promise<WasRemoteStore>}
25
+ * @param [options.onMarkersFetched] {(markers) => void | Promise<void>} given
26
+ * the freshly fetched per-collection encryption markers (by WAS collection
27
+ * id), to refresh the offline marker cache
28
+ * @returns {Promise<WasSyncBootstrap>}
17
29
  */
18
- export async function startWasSync({ parsed, zcapClient, collections, localStore, syncController, onRemoteChange, onAuthError }) {
30
+ export async function startWasSync({ parsed, zcapClient, collections, localStore, syncController, onRemoteChange, sharedCollections = [], identityKeys, onAuthError, onMarkersFetched }) {
19
31
  const remoteStore = WasRemoteStore.fromGrants({
20
32
  parsed,
21
33
  zcapClient,
@@ -27,7 +39,10 @@ export async function startWasSync({ parsed, zcapClient, collections, localStore
27
39
  // collections it does not apply to (reported ok + skipped): the encryption
28
40
  // marker skips public collections, the indexes declaration skips private
29
41
  // ones and public ones with no declared indexes.
30
- await Promise.all(Object.keys(parsed.byCollectionId).map(async (collectionId) => {
42
+ const sharedIds = new Set(sharedCollections.map(entry => entry.id));
43
+ await Promise.all(Object.keys(parsed.byCollectionId)
44
+ .filter(collectionId => !sharedIds.has(collectionId))
45
+ .map(async (collectionId) => {
31
46
  const marker = await remoteStore.markCollectionEncrypted(collectionId);
32
47
  if (!marker.ok) {
33
48
  console.warn(`Encryption marker PUT not authorized for "${collectionId}" (status ${marker.status ?? 'n/a'}).`);
@@ -37,12 +52,63 @@ export async function startWasSync({ parsed, zcapClient, collections, localStore
37
52
  console.warn(`Indexes declaration PUT not authorized for "${collectionId}" (status ${indexes.status ?? 'n/a'}).`);
38
53
  }
39
54
  }));
55
+ // Fetch each granted private collection's encryption marker: rebuild that
56
+ // collection's cipher when its epoch roster differs from what the local store
57
+ // opened with (a wallet-side rotation, or first-ever epochs), and hand the
58
+ // fresh set to the marker-cache refresher so an offline session can rebuild
59
+ // its epoch-aware ciphers without a live read.
60
+ const privateIds = collections
61
+ .filter(collection => collection.visibility !== 'public')
62
+ .map(collection => collection.id)
63
+ .filter(id => parsed.byCollectionId[id] !== undefined);
64
+ const markers = {};
65
+ await Promise.all(privateIds.map(async (collectionId) => {
66
+ const encryption = await remoteStore.readCollectionEncryption(collectionId);
67
+ if (encryption) {
68
+ markers[collectionId] = encryption;
69
+ await localStore.applyRemoteMarker({ collectionId, encryption });
70
+ }
71
+ }));
72
+ // Install the one-shot epoch refresher so a decrypt that meets an unseen epoch
73
+ // (a rotation on another device) re-reads the marker and rebuilds the cipher.
74
+ localStore.setEpochRefresher(collectionId => remoteStore.readCollectionEncryption(collectionId));
75
+ if (onMarkersFetched) {
76
+ await onMarkersFetched(markers);
77
+ }
78
+ // Shared collections stay entirely out of replication: one read-only reader
79
+ // each, opened over the delegated read zcap and the collection's epoch
80
+ // roster. Every failure mode here is a warn-and-skip -- an uncovered grant,
81
+ // a collection with no roster, an app that is not (or is no longer) a
82
+ // recipient -- so a removed share degrades one reader, never the session.
83
+ const sharedReaders = {};
84
+ for (const { key, id } of sharedCollections) {
85
+ if (!parsed.byCollectionId[id]) {
86
+ console.warn(`Skipping shared collection "${id}": no delegated capability covers it.`);
87
+ continue;
88
+ }
89
+ if (!identityKeys) {
90
+ console.warn(`Skipping shared collection "${id}": no identity key-agreement key was ` +
91
+ `supplied to decrypt it with.`);
92
+ continue;
93
+ }
94
+ try {
95
+ sharedReaders[key] = await SharedCollectionReader.open({
96
+ remoteStore,
97
+ keyAgreementKey: identityKeys.keyAgreementKey,
98
+ keyResolver: identityKeys.keyResolver,
99
+ collectionId: id
100
+ });
101
+ }
102
+ catch (err) {
103
+ console.warn(`Skipping shared collection "${id}":`, err);
104
+ }
105
+ }
40
106
  await syncController.start({
41
107
  remoteStore,
42
108
  localStore,
43
109
  onRemoteChange,
44
110
  ...(onAuthError && { onAuthError })
45
111
  });
46
- return remoteStore;
112
+ return { remoteStore, sharedCollections: sharedReaders };
47
113
  }
48
114
  //# sourceMappingURL=wasSync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wasSync.js","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAIpD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EACjC,MAAM,EACN,UAAU,EACV,WAAW,EACX,UAAU,EACV,cAAc,EACd,cAAc,EACd,WAAW,EAYZ;IACC,MAAM,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC;QAC5C,MAAM;QACN,UAAU;QACV,WAAW;KACZ,CAAC,CAAA;IAEF,2EAA2E;IAC3E,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,iDAAiD;IACjD,MAAM,OAAO,CAAC,GAAG,CACf,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,KAAK,EAAC,YAAY,EAAC,EAAE;QAC1D,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,uBAAuB,CAAC,YAAY,CAAC,CAAA;QACtE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CACV,6CAA6C,YAAY,aAAa,MAAM,CAAC,MAAM,IAAI,KAAK,IAAI,CACjG,CAAA;QACH,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,YAAY,CAAC,CAAA;QACxE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;YAChB,OAAO,CAAC,IAAI,CACV,+CAA+C,YAAY,aAAa,OAAO,CAAC,MAAM,IAAI,KAAK,IAAI,CACpG,CAAA;QACH,CAAC;IACH,CAAC,CAAC,CACH,CAAA;IAED,MAAM,cAAc,CAAC,KAAK,CAAC;QACzB,WAAW;QACX,UAAU;QACV,cAAc;QACd,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;KACpC,CAAC,CAAA;IACF,OAAO,WAAW,CAAA;AACpB,CAAC"}
1
+ {"version":3,"file":"wasSync.js","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,7 @@
16
16
  * storage layer above needs no encrypted-vs-plaintext fork.
17
17
  */
18
18
  import type { IKeyAgreementKey, IKeyResolver } from '@interop/data-integrity-core';
19
+ import type { CollectionEncryption } from '@interop/was-client';
19
20
  import type { Json } from './types.js';
20
21
  /**
21
22
  * A per-collection document cipher. `encrypt` is the create path (mints a random
@@ -70,21 +71,36 @@ export declare function createPlaintextDocCodec({ collectionId }: {
70
71
  collectionId: string;
71
72
  }): DocCipher;
72
73
  /**
73
- * Builds a {@link DocCipher} for one collection from its derived key material
74
- * (the per-collection X25519 key agreement key). Keys are supplied directly (no
74
+ * Builds a {@link DocCipher} for one collection from the caller's derived key
75
+ * material (the app's identity X25519 key agreement key, the same one every
76
+ * other collection uses). Keys are supplied directly (no
75
77
  * keystore lookup). `idDerivation: 'random'` mints a stable random id updated in
76
78
  * place via `sequence` -- the mutable head-document model every entity here uses
77
79
  * (constant bump / toggle / re-categorize edits).
78
80
  *
81
+ * Delegates to `@interop/was-client`'s `createEdvDocCipher`: with no
82
+ * `encryption` marker (or a marker with no key epochs) the cipher is
83
+ * single-recipient (the key-agreement key encrypts and decrypts directly, the
84
+ * behavior every collection has had); with epochs on the marker the cipher
85
+ * becomes multi-recipient -- writes stamp the marker's current epoch and reads
86
+ * route by the envelope's recipient key id, while a pre-epoch envelope still
87
+ * decrypts through the single-key path (a permanent tolerance, not a migration
88
+ * shim). The returned cipher's shape matches {@link DocCipher} exactly; only the
89
+ * nominal `Json` origin differs, so it crosses the boundary with a cast.
90
+ *
79
91
  * @param options {object}
80
92
  * @param options.keyAgreementKey {IKeyAgreementKey}
81
93
  * @param options.keyResolver {IKeyResolver}
82
94
  * @param options.collectionId {string} labels errors; the codec is agnostic
95
+ * @param [options.encryption] {CollectionEncryption} the collection's
96
+ * encryption marker; when it carries key epochs the cipher becomes
97
+ * multi-recipient
83
98
  * @returns {Promise<DocCipher>}
84
99
  */
85
- export declare function createDocCipher({ keyAgreementKey, keyResolver, collectionId }: {
100
+ export declare function createDocCipher({ keyAgreementKey, keyResolver, collectionId, encryption }: {
86
101
  keyAgreementKey: IKeyAgreementKey;
87
102
  keyResolver: IKeyResolver;
88
103
  collectionId: string;
104
+ encryption?: CollectionEncryption;
89
105
  }): Promise<DocCipher>;
90
106
  //# sourceMappingURL=docCipher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"docCipher.d.ts","sourceRoot":"","sources":["../../src/sync/docCipher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AAErC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAA;IACzE,aAAa,CAAC,OAAO,EAAE;QACrB,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,IAAI,CAAA;QACV,OAAO,EAAE,IAAI,CAAA;KACd,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAA;IAC3C,OAAO,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACpD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,OAAO,CAMnE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,YAAY,EACb,EAAE;IACD,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,SAAS,CAiCZ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CAAC,EACpC,eAAe,EACf,WAAW,EACX,YAAY,EACb,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,OAAO,CAAC,SAAS,CAAC,CA+ErB"}
1
+ {"version":3,"file":"docCipher.d.ts","sourceRoot":"","sources":["../../src/sync/docCipher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAE/D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAA;IACzE,aAAa,CAAC,OAAO,EAAE;QACrB,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,IAAI,CAAA;QACV,OAAO,EAAE,IAAI,CAAA;KACd,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC,CAAA;IAC3C,OAAO,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACpD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,OAAO,CAMnE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,YAAY,EACb,EAAE;IACD,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,SAAS,CAiCZ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,eAAe,CAAC,EACpC,eAAe,EACf,WAAW,EACX,YAAY,EACZ,UAAU,EACX,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,oBAAoB,CAAA;CAClC,GAAG,OAAO,CAAC,SAAS,CAAC,CASrB"}
@@ -1,4 +1,4 @@
1
- import { createEdvEncryption } from '@interop/was-client/edv';
1
+ import { createEdvDocCipher } from '@interop/was-client/edv';
2
2
  /**
3
3
  * Whether a stored body is an EDV encryption envelope (carries an object `jwe`)
4
4
  * rather than plaintext. Lets read paths stay tolerant of any legacy plaintext
@@ -57,75 +57,40 @@ export function createPlaintextDocCodec({ collectionId }) {
57
57
  };
58
58
  }
59
59
  /**
60
- * Builds a {@link DocCipher} for one collection from its derived key material
61
- * (the per-collection X25519 key agreement key). Keys are supplied directly (no
60
+ * Builds a {@link DocCipher} for one collection from the caller's derived key
61
+ * material (the app's identity X25519 key agreement key, the same one every
62
+ * other collection uses). Keys are supplied directly (no
62
63
  * keystore lookup). `idDerivation: 'random'` mints a stable random id updated in
63
64
  * place via `sequence` -- the mutable head-document model every entity here uses
64
65
  * (constant bump / toggle / re-categorize edits).
65
66
  *
67
+ * Delegates to `@interop/was-client`'s `createEdvDocCipher`: with no
68
+ * `encryption` marker (or a marker with no key epochs) the cipher is
69
+ * single-recipient (the key-agreement key encrypts and decrypts directly, the
70
+ * behavior every collection has had); with epochs on the marker the cipher
71
+ * becomes multi-recipient -- writes stamp the marker's current epoch and reads
72
+ * route by the envelope's recipient key id, while a pre-epoch envelope still
73
+ * decrypts through the single-key path (a permanent tolerance, not a migration
74
+ * shim). The returned cipher's shape matches {@link DocCipher} exactly; only the
75
+ * nominal `Json` origin differs, so it crosses the boundary with a cast.
76
+ *
66
77
  * @param options {object}
67
78
  * @param options.keyAgreementKey {IKeyAgreementKey}
68
79
  * @param options.keyResolver {IKeyResolver}
69
80
  * @param options.collectionId {string} labels errors; the codec is agnostic
81
+ * @param [options.encryption] {CollectionEncryption} the collection's
82
+ * encryption marker; when it carries key epochs the cipher becomes
83
+ * multi-recipient
70
84
  * @returns {Promise<DocCipher>}
71
85
  */
72
- export async function createDocCipher({ keyAgreementKey, keyResolver, collectionId }) {
73
- const provider = createEdvEncryption({
74
- resolveKeys: async () => null,
75
- idDerivation: 'random'
76
- });
77
- const codec = await provider.codecFor({
78
- spaceId: 'local',
86
+ export async function createDocCipher({ keyAgreementKey, keyResolver, collectionId, encryption }) {
87
+ const cipher = await createEdvDocCipher({
88
+ keyAgreementKey,
89
+ keyResolver,
79
90
  collectionId,
80
- scheme: 'edv',
81
- keys: { keyAgreementKey, keyResolver }
91
+ idDerivation: 'random',
92
+ ...(encryption && { encryption })
82
93
  });
83
- if (!codec) {
84
- throw new Error(`Could not build the EDV cipher for collection "${collectionId}".`);
85
- }
86
- // Parses the codec's `EncodedWrite` (id + envelope body bytes) into the
87
- // stored `{ id, envelope }` shape. Shared by the create and update paths.
88
- const readEncoded = (encoded) => {
89
- if (typeof encoded.id !== 'string' ||
90
- !(encoded.body instanceof Uint8Array)) {
91
- throw new Error(`EDV encrypt for collection "${collectionId}" returned no id/envelope body.`);
92
- }
93
- const envelope = JSON.parse(new TextDecoder().decode(encoded.body));
94
- return { id: encoded.id, envelope };
95
- };
96
- return {
97
- async encrypt({ data }) {
98
- // `encode` with no caller id is the create path: encrypt, then use the
99
- // minted random id.
100
- const encoded = await codec.encode({
101
- data: data
102
- });
103
- return readEncoded(encoded);
104
- },
105
- async encryptUpdate({ id, data, current }) {
106
- // The update path: hand the codec the prior stored envelope so it advances
107
- // `sequence` from it and re-encrypts under the same id. The codec reads
108
- // `current.data` (the prior envelope) and its `etag` header (unused here;
109
- // the wire `If-Match` is derived from the synced-doc `version`, not this).
110
- const priorResponse = {
111
- data: current,
112
- json: async () => current,
113
- headers: { get: () => null }
114
- };
115
- const encoded = await codec.encode({
116
- id,
117
- data: data,
118
- current: priorResponse
119
- });
120
- return readEncoded(encoded);
121
- },
122
- async decrypt({ envelope }) {
123
- const response = {
124
- data: envelope,
125
- json: async () => envelope
126
- };
127
- return (await codec.decode(response));
128
- }
129
- };
94
+ return cipher;
130
95
  }
131
96
  //# sourceMappingURL=docCipher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"docCipher.js","sourceRoot":"","sources":["../../src/sync/docCipher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"docCipher.js","sourceRoot":"","sources":["../../src/sync/docCipher.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAmB5D;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAsB;IACxD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACpE,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,GAAG,GAAI,IAA0B,CAAC,GAAG,CAAA;IAC3C,OAAO,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAA;AAChD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CAAC,EACtC,YAAY,EAGb;IACC,MAAM,SAAS,GAAG,CAAC,IAAU,EAAU,EAAE;QACvC,MAAM,EAAE,GAAI,IAAgC,EAAE,EAAE,CAAA;QAChD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CACb,kCAAkC,YAAY,2BAA2B,CAC1E,CAAA;QACH,CAAC;QACD,OAAO,EAAE,CAAA;IACX,CAAC,CAAA;IACD,MAAM,eAAe,GAAG,CAAC,IAAU,EAAQ,EAAE;QAC3C,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CACb,eAAe,YAAY,yCAAyC;gBAClE,yBAAyB,CAC5B,CAAA;QACH,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,EAAkB;YACpC,OAAO,EAAE,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;QAChD,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,IAAI,EAA8B;YAC1D,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;QAC/B,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAsB;YAC5C,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAA;QAClC,CAAC;KACF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EACpC,eAAe,EACf,WAAW,EACX,YAAY,EACZ,UAAU,EAMX;IACC,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC;QACtC,eAAe;QACf,WAAW;QACX,YAAY;QACZ,YAAY,EAAE,QAAQ;QACtB,GAAG,CAAC,UAAU,IAAI,EAAE,UAAU,EAAE,CAAC;KAClC,CAAC,CAAA;IACF,OAAO,MAA8B,CAAA;AACvC,CAAC"}
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.3.6",
4
+ "version": "0.5.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "pnpm run clear && tsc",
@@ -46,14 +46,14 @@
46
46
  "types": "dist/index.d.ts",
47
47
  "sideEffects": false,
48
48
  "dependencies": {
49
- "@interop/data-integrity-core": "^8.3.0",
49
+ "@interop/data-integrity-core": "^8.4.0",
50
50
  "@interop/ed25519-signature": "^7.1.4",
51
51
  "@interop/ezcap": "^7.4.1",
52
52
  "@interop/security-document-loader": "^9.4.4",
53
53
  "@interop/vc": "^11.0.6",
54
- "@interop/verifier-core": "^3.4.1",
54
+ "@interop/verifier-core": "^3.5.0",
55
55
  "@interop/wallet-core": "^0.5.0",
56
- "@interop/was-client": "^0.19.0",
56
+ "@interop/was-client": "^0.20.0",
57
57
  "@interop/webkms-client": "^14.7.1",
58
58
  "@interop/x25519-key-agreement-key": "^5.2.1",
59
59
  "@scure/base": "^2.2.0",
@@ -109,7 +109,7 @@
109
109
  "typescript-eslint": "^8.59.4",
110
110
  "vite": "^8.0.14",
111
111
  "vitest": "^4.1.7",
112
- "was-teaching-server": "^0.12.0",
112
+ "was-teaching-server": "^0.14.0",
113
113
  "zustand": "^5.0.12"
114
114
  },
115
115
  "publishConfig": {