@interop/was-client 0.18.0 → 0.19.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 (83) hide show
  1. package/README.md +88 -0
  2. package/dist/Collection.d.ts +1 -46
  3. package/dist/Collection.d.ts.map +1 -1
  4. package/dist/Collection.js +77 -79
  5. package/dist/Collection.js.map +1 -1
  6. package/dist/Resource.d.ts +1 -51
  7. package/dist/Resource.d.ts.map +1 -1
  8. package/dist/Resource.js +55 -55
  9. package/dist/Resource.js.map +1 -1
  10. package/dist/Space.d.ts +1 -27
  11. package/dist/Space.d.ts.map +1 -1
  12. package/dist/Space.js +65 -65
  13. package/dist/Space.js.map +1 -1
  14. package/dist/WasClient.d.ts +1 -12
  15. package/dist/WasClient.d.ts.map +1 -1
  16. package/dist/WasClient.js +15 -15
  17. package/dist/WasClient.js.map +1 -1
  18. package/dist/edv/EdvCodec.d.ts +1 -139
  19. package/dist/edv/EdvCodec.d.ts.map +1 -1
  20. package/dist/edv/EdvCodec.js +57 -58
  21. package/dist/edv/EdvCodec.js.map +1 -1
  22. package/dist/edv/WasTransport.d.ts +1 -53
  23. package/dist/edv/WasTransport.d.ts.map +1 -1
  24. package/dist/edv/WasTransport.js +29 -29
  25. package/dist/edv/WasTransport.js.map +1 -1
  26. package/dist/edv/docCipher.d.ts +111 -0
  27. package/dist/edv/docCipher.d.ts.map +1 -0
  28. package/dist/edv/docCipher.js +236 -0
  29. package/dist/edv/docCipher.js.map +1 -0
  30. package/dist/edv/epochKeys.js +2 -2
  31. package/dist/edv/epochKeys.js.map +1 -1
  32. package/dist/edv/index.d.ts +2 -0
  33. package/dist/edv/index.d.ts.map +1 -1
  34. package/dist/edv/index.js +1 -0
  35. package/dist/edv/index.js.map +1 -1
  36. package/dist/errors.d.ts +22 -0
  37. package/dist/errors.d.ts.map +1 -1
  38. package/dist/errors.js +28 -0
  39. package/dist/errors.js.map +1 -1
  40. package/dist/index.d.ts +1 -1
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +1 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/internal/codec.d.ts +1 -2
  45. package/dist/internal/codec.d.ts.map +1 -1
  46. package/dist/internal/codec.js +10 -10
  47. package/dist/internal/codec.js.map +1 -1
  48. package/dist/internal/features.d.ts +1 -12
  49. package/dist/internal/features.d.ts.map +1 -1
  50. package/dist/internal/features.js +8 -8
  51. package/dist/internal/features.js.map +1 -1
  52. package/dist/internal/pagination.d.ts +1 -1
  53. package/dist/internal/pagination.js +1 -1
  54. package/dist/sync/cid.d.ts +31 -0
  55. package/dist/sync/cid.d.ts.map +1 -0
  56. package/dist/sync/cid.js +55 -0
  57. package/dist/sync/cid.js.map +1 -0
  58. package/dist/sync/envelope.d.ts +20 -0
  59. package/dist/sync/envelope.d.ts.map +1 -0
  60. package/dist/sync/envelope.js +17 -0
  61. package/dist/sync/envelope.js.map +1 -0
  62. package/dist/sync/index.d.ts +29 -0
  63. package/dist/sync/index.d.ts.map +1 -0
  64. package/dist/sync/index.js +28 -0
  65. package/dist/sync/index.js.map +1 -0
  66. package/dist/sync/plaintextCipher.d.ts +16 -0
  67. package/dist/sync/plaintextCipher.d.ts.map +1 -0
  68. package/dist/sync/plaintextCipher.js +36 -0
  69. package/dist/sync/plaintextCipher.js.map +1 -0
  70. package/dist/sync/port.d.ts +78 -0
  71. package/dist/sync/port.d.ts.map +1 -0
  72. package/dist/sync/port.js +204 -0
  73. package/dist/sync/port.js.map +1 -0
  74. package/dist/sync/provisioning.d.ts +41 -0
  75. package/dist/sync/provisioning.d.ts.map +1 -0
  76. package/dist/sync/provisioning.js +44 -0
  77. package/dist/sync/provisioning.js.map +1 -0
  78. package/dist/sync/types.d.ts +166 -0
  79. package/dist/sync/types.d.ts.map +1 -0
  80. package/dist/sync/types.js +2 -0
  81. package/dist/sync/types.js.map +1 -0
  82. package/dist/types.d.ts +1 -1
  83. package/package.json +10 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plaintextCipher.js","sourceRoot":"","sources":["../../src/sync/plaintextCipher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAGrC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB,CAAC,EACvC,YAAY,EAGb;IACC,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,EAAkB;YACpC,OAAO,EAAE,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;QACjD,CAAC;QAED,KAAK,CAAC,aAAa;YACjB,MAAM,IAAI,KAAK,CACb,eAAe,YAAY,oCAAoC;gBAC7D,uCAAuC,CAC1C,CAAA;QACH,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAsB;YAC5C,OAAO,QAAQ,CAAA;QACjB,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,78 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * `createWasSyncPort`: the {@link WasSyncPort} implementation over a signed
6
+ * {@link WasClient}, bound to one Space + Collection.
7
+ *
8
+ * Writes and single-resource reads ride the raw, signed `was.request()` escape
9
+ * hatch, which moves the stored body VERBATIM (bypassing the encryption codec).
10
+ * The change feed already ships opaque stored bodies -- plaintext for a
11
+ * plaintext collection, the EDV envelope for an encrypted one -- and push must
12
+ * write those same bytes back unchanged; running them through `resource.put()`
13
+ * would re-encrypt an already-encrypted envelope. Encrypt/decrypt therefore
14
+ * stays a read/write-time concern above the port, and the port itself is
15
+ * collection-agnostic and never touches keys.
16
+ *
17
+ * The pull path rides the client's `Collection.changes()` feed, bound to the
18
+ * same Space + Collection, which produces the byte-identical signed
19
+ * `POST /space/:s/:c/query` (profile `changes`) as a root invocation and, like
20
+ * the raw writes, ships the stored bodies verbatim without decrypting.
21
+ *
22
+ * Conditional writes ride the server's monotonic content `version` (`ETag`),
23
+ * enforced uniformly for plaintext and encrypted resources, so there is no
24
+ * plaintext-vs-encrypted fork. `putContent`/`deleteContent` return the server-
25
+ * acked `version` (parsed from the write's `ETag`, re-read only if the backend
26
+ * sent none), so a caller can record acked revisions immediately.
27
+ */
28
+ import type { WasClient } from '../WasClient.js';
29
+ import type { WasSyncPort } from './types.js';
30
+ /**
31
+ * The request header the server reads a content write's key-epoch id from,
32
+ * stamping it onto the Resource's metadata (an absent header clears any prior
33
+ * stamp). HTTP header names are case-insensitive; the wire form is `WAS-Key-Epoch`.
34
+ */
35
+ export declare const WAS_KEY_EPOCH_HEADER = "WAS-Key-Epoch";
36
+ /**
37
+ * Extracts an HTTP status from a raw ky/ezcap error. `was.request()` rejects on
38
+ * any non-2xx with `err.status` set; this reads it defensively from either the
39
+ * flat `status` or the nested `response.status` shape.
40
+ *
41
+ * @param err {unknown} the caught error
42
+ * @returns {number | undefined}
43
+ */
44
+ export declare function errorStatus(err: unknown): number | undefined;
45
+ /**
46
+ * Formats a numeric content `version` as the quoted strong `ETag` an
47
+ * update-if-unchanged write passes as its `ifMatch` precondition (e.g. `3` to
48
+ * `"3"`). Inverse of {@link parseEtag}.
49
+ *
50
+ * @param version {number}
51
+ * @returns {string}
52
+ */
53
+ export declare function formatEtag(version: number): string;
54
+ /**
55
+ * Parses a quoted strong `ETag` (`"3"`) into its numeric revision, or
56
+ * `undefined` when the header is absent or non-numeric (no such revision yet).
57
+ *
58
+ * @param etag {string | null}
59
+ * @returns {number | undefined}
60
+ */
61
+ export declare function parseEtag(etag: string | null): number | undefined;
62
+ /**
63
+ * Builds a {@link WasSyncPort} bound to one Space + Collection, backed by the
64
+ * caller's signed {@link WasClient}. Requests invoke the client's own root
65
+ * capability (no delegated `capability` is attached).
66
+ *
67
+ * @param options {object}
68
+ * @param options.was {WasClient} the session client (holds the signer)
69
+ * @param options.spaceId {string} the WAS Space id
70
+ * @param options.collectionId {string} the WAS collection id
71
+ * @returns {WasSyncPort}
72
+ */
73
+ export declare function createWasSyncPort({ was, spaceId, collectionId }: {
74
+ was: WasClient;
75
+ spaceId: string;
76
+ collectionId: string;
77
+ }): WasSyncPort;
78
+ //# sourceMappingURL=port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../src/sync/port.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAIhD,OAAO,KAAK,EAIV,WAAW,EAEZ,MAAM,YAAY,CAAA;AAEnB;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,kBAAkB,CAAA;AAWnD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAK5D;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAMjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,GAAG,EACH,OAAO,EACP,YAAY,EACb,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;CACrB,GAAG,WAAW,CA0Jd"}
@@ -0,0 +1,204 @@
1
+ import { writeHeaders } from '../internal/conditional.js';
2
+ import { WasSyncConflictError, WasSyncNotFoundError } from '../errors.js';
3
+ /**
4
+ * The request header the server reads a content write's key-epoch id from,
5
+ * stamping it onto the Resource's metadata (an absent header clears any prior
6
+ * stamp). HTTP header names are case-insensitive; the wire form is `WAS-Key-Epoch`.
7
+ */
8
+ export const WAS_KEY_EPOCH_HEADER = 'WAS-Key-Epoch';
9
+ /**
10
+ * The placeholder `updatedAt` for a 412-conflict re-read whose resource has no
11
+ * `/meta` document yet (its server-managed timestamp is unknown). An epoch-zero
12
+ * ISO string is a valid, sortable timestamp that sorts before every real one --
13
+ * unlike an empty string, which is not a parseable date. The change feed remains
14
+ * the authority on ordering, so this only feeds the one-off conflict entry.
15
+ */
16
+ const UNKNOWN_UPDATED_AT = new Date(0).toISOString();
17
+ /**
18
+ * Extracts an HTTP status from a raw ky/ezcap error. `was.request()` rejects on
19
+ * any non-2xx with `err.status` set; this reads it defensively from either the
20
+ * flat `status` or the nested `response.status` shape.
21
+ *
22
+ * @param err {unknown} the caught error
23
+ * @returns {number | undefined}
24
+ */
25
+ export function errorStatus(err) {
26
+ return (err.status ??
27
+ err.response?.status);
28
+ }
29
+ /**
30
+ * Formats a numeric content `version` as the quoted strong `ETag` an
31
+ * update-if-unchanged write passes as its `ifMatch` precondition (e.g. `3` to
32
+ * `"3"`). Inverse of {@link parseEtag}.
33
+ *
34
+ * @param version {number}
35
+ * @returns {string}
36
+ */
37
+ export function formatEtag(version) {
38
+ return `"${version}"`;
39
+ }
40
+ /**
41
+ * Parses a quoted strong `ETag` (`"3"`) into its numeric revision, or
42
+ * `undefined` when the header is absent or non-numeric (no such revision yet).
43
+ *
44
+ * @param etag {string | null}
45
+ * @returns {number | undefined}
46
+ */
47
+ export function parseEtag(etag) {
48
+ if (!etag) {
49
+ return undefined;
50
+ }
51
+ const revision = Number(etag.replace(/"/g, ''));
52
+ return Number.isFinite(revision) ? revision : undefined;
53
+ }
54
+ /**
55
+ * Builds a {@link WasSyncPort} bound to one Space + Collection, backed by the
56
+ * caller's signed {@link WasClient}. Requests invoke the client's own root
57
+ * capability (no delegated `capability` is attached).
58
+ *
59
+ * @param options {object}
60
+ * @param options.was {WasClient} the session client (holds the signer)
61
+ * @param options.spaceId {string} the WAS Space id
62
+ * @param options.collectionId {string} the WAS collection id
63
+ * @returns {WasSyncPort}
64
+ */
65
+ export function createWasSyncPort({ was, spaceId, collectionId }) {
66
+ const collectionPath = `/space/${spaceId}/${collectionId}`;
67
+ const resourcePath = (id) => `${collectionPath}/${encodeURIComponent(id)}`;
68
+ // Construction is I/O-free (the codec/feature probes are lazy thunks) and
69
+ // `changes()` never resolves the codec, so it ships the stored bodies
70
+ // verbatim -- what this codec-bypassing port requires.
71
+ const changesCollection = was.space(spaceId).collection(collectionId);
72
+ /** Re-reads a resource's raw content body + version (no decrypt, no `/meta`). */
73
+ const readContent = async (id) => {
74
+ let response;
75
+ try {
76
+ response = await was.request({ path: resourcePath(id), method: 'GET' });
77
+ }
78
+ catch (err) {
79
+ if (errorStatus(err) === 404) {
80
+ return null; // absent or tombstoned -- caller treats as deletion-wins
81
+ }
82
+ throw err;
83
+ }
84
+ return {
85
+ version: parseEtag(response.headers.get('etag')) ?? 0,
86
+ updatedAt: UNKNOWN_UPDATED_AT,
87
+ deleted: false,
88
+ data: response.data
89
+ };
90
+ };
91
+ /** Resolves the acked version from a write response, or via a content re-read. */
92
+ const ackedVersion = async (response, id) => {
93
+ const version = parseEtag(response.headers.get('etag'));
94
+ if (version !== undefined) {
95
+ return version;
96
+ }
97
+ return (await readContent(id))?.version ?? 0;
98
+ };
99
+ return {
100
+ async query({ checkpoint, limit }) {
101
+ const page = await changesCollection.changes({ checkpoint, limit });
102
+ return {
103
+ documents: page.documents,
104
+ checkpoint: page.checkpoint
105
+ };
106
+ },
107
+ async putContent({ id, data, ifMatch, ifNoneMatch, epoch }) {
108
+ try {
109
+ const response = await was.request({
110
+ path: resourcePath(id),
111
+ method: 'PUT',
112
+ json: data,
113
+ headers: writeHeaders({
114
+ precondition: { ifMatch, ifNoneMatch },
115
+ epoch
116
+ })
117
+ });
118
+ return await ackedVersion(response, id);
119
+ }
120
+ catch (err) {
121
+ if (errorStatus(err) === 412) {
122
+ throw new WasSyncConflictError();
123
+ }
124
+ throw err;
125
+ }
126
+ },
127
+ async deleteContent({ id, ifMatch }) {
128
+ try {
129
+ const response = await was.request({
130
+ path: resourcePath(id),
131
+ method: 'DELETE',
132
+ headers: writeHeaders({ precondition: { ifMatch } })
133
+ });
134
+ return await ackedVersion(response, id);
135
+ }
136
+ catch (err) {
137
+ const status = errorStatus(err);
138
+ if (status === 404) {
139
+ throw new WasSyncNotFoundError();
140
+ }
141
+ if (status === 412) {
142
+ throw new WasSyncConflictError();
143
+ }
144
+ throw err;
145
+ }
146
+ },
147
+ async putMeta({ id, custom, ifMatch, ifNoneMatch }) {
148
+ try {
149
+ await was.request({
150
+ path: `${resourcePath(id)}/meta`,
151
+ method: 'PUT',
152
+ json: { custom },
153
+ headers: writeHeaders({ precondition: { ifMatch, ifNoneMatch } })
154
+ });
155
+ }
156
+ catch (err) {
157
+ if (errorStatus(err) === 412) {
158
+ throw new WasSyncConflictError();
159
+ }
160
+ throw err;
161
+ }
162
+ },
163
+ async get({ id }) {
164
+ const master = await readContent(id);
165
+ if (master === null) {
166
+ return null;
167
+ }
168
+ // Metadata re-read (best-effort): the `/meta` body carries the server-
169
+ // managed `updatedAt`, the creator DID, the key-epoch id, and the user-
170
+ // writable `custom`, plus its own `metaVersion` ETag. A resource with no
171
+ // metadata yet 404s here; only a hard error propagates.
172
+ try {
173
+ const metaResponse = await was.request({
174
+ path: `${resourcePath(id)}/meta`,
175
+ method: 'GET'
176
+ });
177
+ const metaBody = metaResponse.data;
178
+ if (metaBody?.updatedAt) {
179
+ master.updatedAt = metaBody.updatedAt;
180
+ }
181
+ if (metaBody?.createdBy !== undefined) {
182
+ master.createdBy = metaBody.createdBy;
183
+ }
184
+ if (metaBody?.epoch !== undefined) {
185
+ master.epoch = metaBody.epoch;
186
+ }
187
+ if (metaBody?.custom !== undefined) {
188
+ master.custom = metaBody.custom;
189
+ }
190
+ const metaVersion = parseEtag(metaResponse.headers.get('etag'));
191
+ if (metaVersion !== undefined) {
192
+ master.metaVersion = metaVersion;
193
+ }
194
+ }
195
+ catch (err) {
196
+ if (errorStatus(err) !== 404) {
197
+ throw err;
198
+ }
199
+ }
200
+ return master;
201
+ }
202
+ };
203
+ }
204
+ //# sourceMappingURL=port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/sync/port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * Idempotent Space + Collection provisioning for a synced collection. Re-running
6
+ * is safe: the server upserts the Space and allows a late encryption-marker
7
+ * declaration, so reconnecting an existing account is a no-op upgrade.
8
+ */
9
+ import type { WasClient } from '../WasClient.js';
10
+ /**
11
+ * Ensures the controller's Space exists and one synced collection is configured.
12
+ * An `'edv'` collection declares the encryption marker `{ scheme: 'edv' }` (so
13
+ * the server stores only ciphertext it can never decrypt); a `'plaintext'`
14
+ * collection is configured without one, with `force` so the marker-less upsert
15
+ * can create a fresh collection (running with the root capability, a 404 from
16
+ * the pre-merge describe really means absent). A public collection additionally
17
+ * gets a collection-level world-read grant (`setPublic`), which is what makes a
18
+ * resource URL in it resolve for anyone. Runs full-tier -- the client invokes
19
+ * its own root capability.
20
+ *
21
+ * @param options {object}
22
+ * @param options.was {WasClient}
23
+ * @param options.spaceId {string}
24
+ * @param options.controllerDid {string} the Space controller (e.g. `did:key`)
25
+ * @param options.collectionId {string} the WAS collection id
26
+ * @param [options.encryption] {'edv' | 'plaintext'} defaults to `'edv'`
27
+ * @param [options.isPublic] {boolean} grant collection-level world read
28
+ * @param [options.spaceName] {string} the Space display name; defaults to
29
+ * `'WAS Space'`
30
+ * @returns {Promise<void>}
31
+ */
32
+ export declare function ensureSpaceAndCollection({ was, spaceId, controllerDid, collectionId, encryption, isPublic, spaceName }: {
33
+ was: WasClient;
34
+ spaceId: string;
35
+ controllerDid: string;
36
+ collectionId: string;
37
+ encryption?: 'edv' | 'plaintext';
38
+ isPublic?: boolean;
39
+ spaceName?: string;
40
+ }): Promise<void>;
41
+ //# sourceMappingURL=provisioning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provisioning.d.ts","sourceRoot":"","sources":["../../src/sync/provisioning.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,wBAAwB,CAAC,EAC7C,GAAG,EACH,OAAO,EACP,aAAa,EACb,YAAY,EACZ,UAAkB,EAClB,QAAgB,EAChB,SAAuB,EACxB,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,KAAK,GAAG,WAAW,CAAA;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BhB"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Ensures the controller's Space exists and one synced collection is configured.
3
+ * An `'edv'` collection declares the encryption marker `{ scheme: 'edv' }` (so
4
+ * the server stores only ciphertext it can never decrypt); a `'plaintext'`
5
+ * collection is configured without one, with `force` so the marker-less upsert
6
+ * can create a fresh collection (running with the root capability, a 404 from
7
+ * the pre-merge describe really means absent). A public collection additionally
8
+ * gets a collection-level world-read grant (`setPublic`), which is what makes a
9
+ * resource URL in it resolve for anyone. Runs full-tier -- the client invokes
10
+ * its own root capability.
11
+ *
12
+ * @param options {object}
13
+ * @param options.was {WasClient}
14
+ * @param options.spaceId {string}
15
+ * @param options.controllerDid {string} the Space controller (e.g. `did:key`)
16
+ * @param options.collectionId {string} the WAS collection id
17
+ * @param [options.encryption] {'edv' | 'plaintext'} defaults to `'edv'`
18
+ * @param [options.isPublic] {boolean} grant collection-level world read
19
+ * @param [options.spaceName] {string} the Space display name; defaults to
20
+ * `'WAS Space'`
21
+ * @returns {Promise<void>}
22
+ */
23
+ export async function ensureSpaceAndCollection({ was, spaceId, controllerDid, collectionId, encryption = 'edv', isPublic = false, spaceName = 'WAS Space' }) {
24
+ const space = was.space(spaceId);
25
+ try {
26
+ await space.configure({ name: spaceName, controller: controllerDid });
27
+ }
28
+ catch (err) {
29
+ throw new Error(`Failed to configure WAS space "${spaceId}" for "${controllerDid}".`, { cause: err });
30
+ }
31
+ try {
32
+ const collection = space.collection(collectionId);
33
+ await collection.configure(encryption === 'edv'
34
+ ? { name: collectionId, encryption: { scheme: 'edv' } }
35
+ : { name: collectionId, force: true });
36
+ if (isPublic) {
37
+ await collection.setPublic();
38
+ }
39
+ }
40
+ catch (err) {
41
+ throw new Error(`Failed to configure collection "${collectionId}" in space "${spaceId}".`, { cause: err });
42
+ }
43
+ }
44
+ //# sourceMappingURL=provisioning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provisioning.js","sourceRoot":"","sources":["../../src/sync/provisioning.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,EAC7C,GAAG,EACH,OAAO,EACP,aAAa,EACb,YAAY,EACZ,UAAU,GAAG,KAAK,EAClB,QAAQ,GAAG,KAAK,EAChB,SAAS,GAAG,WAAW,EASxB;IACC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAEhC,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,CAAA;IACvE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,kCAAkC,OAAO,UAAU,aAAa,IAAI,EACpE,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAA;IACH,CAAC;IAED,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,YAAY,CAAC,CAAA;QACjD,MAAM,UAAU,CAAC,SAAS,CACxB,UAAU,KAAK,KAAK;YAClB,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;YACvD,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,CACxC,CAAA;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,UAAU,CAAC,SAAS,EAAE,CAAA;QAC9B,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,mCAAmC,YAAY,eAAe,OAAO,IAAI,EACzE,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAA;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,166 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * Wire and port types for cross-replica WAS synchronization.
6
+ *
7
+ * The change-feed wire model (`SyncCheckpoint`, `WireDoc`, and one page of the
8
+ * feed) is the shared WAS data model from `@interop/storage-core`, re-exported
9
+ * here under replication-facing names so a sync consumer imports one module.
10
+ * `WasSyncPort` and `MasterState` are the injectable access seam the change
11
+ * engine depends on, and `DocCipher` is the encrypt/decrypt seam a per-
12
+ * collection cipher implements.
13
+ */
14
+ import type { ChangeDocument, ChangesCheckpoint, ChangesPage } from '@interop/storage-core';
15
+ import type { Json } from '../types.js';
16
+ export type { Json };
17
+ /**
18
+ * The keyset position in the change feed: the `{ id, updatedAt }` of the last
19
+ * document a pull returned, passed back verbatim to resume strictly after it.
20
+ * `id` is the total-order tiebreaker within a single `updatedAt`. This is the
21
+ * shared `ChangesCheckpoint` from `@interop/storage-core` -- server time only,
22
+ * an opaque position token, never compared against a device clock.
23
+ */
24
+ export type SyncCheckpoint = ChangesCheckpoint;
25
+ /**
26
+ * One document as it travels on the `changes` feed wire: `id` is the WAS
27
+ * resource id, `version` the content revision (the push `If-Match` ETag), and
28
+ * the stored body is under `data`. A tombstone carries `_deleted: true` with no
29
+ * `data`. This is the shared `ChangeDocument` from `@interop/storage-core`; on
30
+ * an encrypted collection `data`/`custom` are the opaque stored envelope, moved
31
+ * verbatim (decrypt is a projection-time concern the engine's `DocCipher`
32
+ * handles, never the port).
33
+ */
34
+ export type WireDoc = ChangeDocument;
35
+ /**
36
+ * One page of the `changes` feed -- the return shape of {@link WasSyncPort.query}
37
+ * (the shared `ChangesPage`): the page's `documents` and its resume
38
+ * `checkpoint`, or `checkpoint: null` for an empty (no-change) page.
39
+ */
40
+ export type SyncPage = ChangesPage;
41
+ /**
42
+ * The current master state of a single resource, read back for the 412-conflict
43
+ * path ({@link WasSyncPort.get}). `deleted` distinguishes a tombstone from a
44
+ * live resource. `updatedAt`, `metaVersion`, `custom`, `createdBy`, and `epoch`
45
+ * are populated from the resource's `/meta` document when it exists; a resource
46
+ * with no metadata yet reports an epoch-zero `updatedAt` placeholder (a valid,
47
+ * sortable timestamp -- the change feed remains the authority on ordering).
48
+ */
49
+ export interface MasterState {
50
+ version: number;
51
+ updatedAt: string;
52
+ deleted: boolean;
53
+ metaVersion?: number;
54
+ data?: Json;
55
+ custom?: Json;
56
+ createdBy?: string;
57
+ epoch?: string;
58
+ }
59
+ /**
60
+ * The injected WAS-access seam. `createWasSyncPort` implements this over
61
+ * `@interop/was-client`'s `was.request()` and the `Collection.changes()` feed;
62
+ * a change engine depends only on this interface. Every method moves the stored
63
+ * body verbatim -- no codec, no key handling.
64
+ *
65
+ * `putMeta` is optional: a replica that syncs only content (never the user-
66
+ * writable `/meta` `custom`) may omit it. All other methods are required.
67
+ */
68
+ export interface WasSyncPort {
69
+ /**
70
+ * Pulls one page of the `changes` feed. Omit `checkpoint` for the first page.
71
+ * Returns the page's `documents` and its resume `checkpoint`, or
72
+ * `checkpoint: null` for an empty (no-change) page.
73
+ */
74
+ query(options: {
75
+ checkpoint?: SyncCheckpoint;
76
+ limit: number;
77
+ }): Promise<{
78
+ documents: WireDoc[];
79
+ checkpoint: SyncCheckpoint | null;
80
+ }>;
81
+ /**
82
+ * Conditionally writes the content body verbatim (`PUT /:id`). Pass
83
+ * `ifNoneMatch: true` for create-if-absent, or `ifMatch` (a quoted ETag over
84
+ * the content `version`) for update-if-unchanged. `epoch` stamps the opaque
85
+ * key-epoch id the body was encrypted under (absent clears any prior stamp).
86
+ * Returns the new server `version` (parsed from the write's `ETag`). Throws
87
+ * {@link WasSyncConflictError} on `412`.
88
+ */
89
+ putContent(options: {
90
+ id: string;
91
+ data: Json;
92
+ ifMatch?: string;
93
+ ifNoneMatch?: boolean;
94
+ epoch?: string;
95
+ }): Promise<number>;
96
+ /**
97
+ * Conditionally deletes a resource (writes a tombstone; `DELETE /:id`). Pass
98
+ * `ifMatch` (a quoted ETag over the content `version`) to delete only if
99
+ * unchanged. Returns the tombstone's new server `version`. Throws
100
+ * {@link WasSyncConflictError} on `412`, {@link WasSyncNotFoundError} on `404`
101
+ * (already gone -- a settled outcome for a delete).
102
+ */
103
+ deleteContent(options: {
104
+ id: string;
105
+ ifMatch?: string;
106
+ }): Promise<number>;
107
+ /**
108
+ * Conditionally writes the user-writable metadata `custom` (`PUT /:id/meta`).
109
+ * Optional -- present only on a port that syncs metadata. Throws
110
+ * {@link WasSyncConflictError} on `412`.
111
+ */
112
+ putMeta?(options: {
113
+ id: string;
114
+ custom: Json;
115
+ ifMatch?: string;
116
+ ifNoneMatch?: boolean;
117
+ }): Promise<void>;
118
+ /**
119
+ * Re-reads a single resource's current master state for the 412-conflict
120
+ * assembler. Returns `null` when the resource is genuinely absent OR a
121
+ * tombstone (the server's `GET` returns `404` for both -- indistinguishable,
122
+ * mapped to deletion-wins by the callers).
123
+ */
124
+ get(options: {
125
+ id: string;
126
+ }): Promise<MasterState | null>;
127
+ }
128
+ /**
129
+ * A per-collection document cipher: encrypts a JSON document into its stored
130
+ * body (minting the resource id) and decrypts a stored body back. Minting the
131
+ * id once, at write time, is what makes the same document converge on the same
132
+ * bytes -- and the same content-derived id -- on every replica.
133
+ *
134
+ * The members reconcile a plaintext (identity) cipher, a single-recipient EDV
135
+ * cipher, and a multi-recipient (key-epoch) EDV cipher:
136
+ *
137
+ * - `encrypt` may surface the `epoch` id a multi-recipient write encrypted
138
+ * under (the marker's `currentEpoch`); absent on a single-key or plaintext
139
+ * cipher.
140
+ * - `encryptUpdate` is optional -- present only for a mutable, random-id
141
+ * collection that re-encrypts a head document in place under its existing id
142
+ * (advancing the envelope `sequence`). A content-addressed cipher (plaintext
143
+ * or `idDerivation: 'content'`) either omits it or throws: a changed document
144
+ * is a different id, never an in-place update.
145
+ */
146
+ export interface DocCipher {
147
+ encrypt(options: {
148
+ data: Json;
149
+ }): Promise<{
150
+ id: string;
151
+ envelope: Json;
152
+ epoch?: string;
153
+ }>;
154
+ encryptUpdate?(options: {
155
+ id: string;
156
+ data: Json;
157
+ current: Json;
158
+ }): Promise<{
159
+ id: string;
160
+ envelope: Json;
161
+ }>;
162
+ decrypt(options: {
163
+ envelope: Json;
164
+ }): Promise<Json>;
165
+ }
166
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;GASG;AACH,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,WAAW,EACZ,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC,YAAY,EAAE,IAAI,EAAE,CAAA;AAEpB;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,iBAAiB,CAAA;AAE9C;;;;;;;;GAQG;AACH,MAAM,MAAM,OAAO,GAAG,cAAc,CAAA;AAEpC;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAA;AAElC;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,MAAM,CAAC,EAAE,IAAI,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE;QAAE,UAAU,CAAC,EAAE,cAAc,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACtE,SAAS,EAAE,OAAO,EAAE,CAAA;QACpB,UAAU,EAAE,cAAc,GAAG,IAAI,CAAA;KAClC,CAAC,CAAA;IAEF;;;;;;;OAOG;IACH,UAAU,CAAC,OAAO,EAAE;QAClB,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,IAAI,CAAA;QACV,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,WAAW,CAAC,EAAE,OAAO,CAAA;QACrB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAEnB;;;;;;OAMG;IACH,aAAa,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAEzE;;;;OAIG;IACH,OAAO,CAAC,CAAC,OAAO,EAAE;QAChB,EAAE,EAAE,MAAM,CAAA;QACV,MAAM,EAAE,IAAI,CAAA;QACZ,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,WAAW,CAAC,EAAE,OAAO,CAAA;KACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEjB;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAA;CAC1D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,OAAO,EAAE;QACf,IAAI,EAAE,IAAI,CAAA;KACX,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC3D,aAAa,CAAC,CAAC,OAAO,EAAE;QACtB,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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":""}
package/dist/types.d.ts CHANGED
@@ -28,7 +28,7 @@ import type { BackendReference, CollectionEncryption } from '@interop/storage-co
28
28
  * The client-writable fields of a Collection Description -- the shape shared
29
29
  * by `Collection.configure` and `Collection.replaceDescription` and the single
30
30
  * place a new writable field is declared (the body/echo inclusion rule lives
31
- * in `Collection._writableFields`).
31
+ * in `Collection.#writableFields`).
32
32
  */
33
33
  export interface CollectionWritableFields {
34
34
  name?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@interop/was-client",
3
3
  "description": "A developer-friendly client for Wallet Attached Storage (WAS) servers.",
4
- "version": "0.18.0",
4
+ "version": "0.19.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "pnpm run clear && tsc",
@@ -35,6 +35,12 @@
35
35
  "react-native": "./dist/edv/index.js",
36
36
  "import": "./dist/edv/index.js",
37
37
  "default": "./dist/edv/index.js"
38
+ },
39
+ "./sync": {
40
+ "types": "./dist/sync/index.d.ts",
41
+ "react-native": "./dist/sync/index.js",
42
+ "import": "./dist/sync/index.js",
43
+ "default": "./dist/sync/index.js"
38
44
  }
39
45
  },
40
46
  "module": "dist/index.js",
@@ -50,7 +56,9 @@
50
56
  "@interop/minimal-cipher": "^7.8.0",
51
57
  "@interop/storage-core": "^0.3.8",
52
58
  "@interop/x25519-key-agreement-key": "^5.2.1",
53
- "@scure/base": "^2.2.0"
59
+ "@noble/hashes": "^2.2.0",
60
+ "@scure/base": "^2.2.0",
61
+ "json-canonicalize": "^2.0.0"
54
62
  },
55
63
  "devDependencies": {
56
64
  "@eslint/js": "^10.0.1",