@interop/wallet-core 0.83.1 → 0.85.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 (52) hide show
  1. package/README.md +13 -3
  2. package/dist/menders/derive.d.ts +1 -0
  3. package/dist/menders/derive.d.ts.map +1 -1
  4. package/dist/menders/derive.js +4 -1
  5. package/dist/menders/derive.js.map +1 -1
  6. package/dist/menders/ids.d.ts +1 -1
  7. package/dist/menders/ids.d.ts.map +1 -1
  8. package/dist/menders/ids.js +3 -1
  9. package/dist/menders/ids.js.map +1 -1
  10. package/dist/menders/vocabulary.d.ts +4 -2
  11. package/dist/menders/vocabulary.d.ts.map +1 -1
  12. package/dist/menders/vocabulary.js +5 -2
  13. package/dist/menders/vocabulary.js.map +1 -1
  14. package/dist/space/collections.d.ts +17 -2
  15. package/dist/space/collections.d.ts.map +1 -1
  16. package/dist/space/collections.js +26 -2
  17. package/dist/space/collections.js.map +1 -1
  18. package/dist/space/index.d.ts +1 -1
  19. package/dist/space/index.d.ts.map +1 -1
  20. package/dist/space/index.js +1 -1
  21. package/dist/space/index.js.map +1 -1
  22. package/dist/sync/engine.d.ts +11 -0
  23. package/dist/sync/engine.d.ts.map +1 -1
  24. package/dist/sync/engine.js +2 -0
  25. package/dist/sync/engine.js.map +1 -1
  26. package/dist/sync/pull.d.ts +8 -2
  27. package/dist/sync/pull.d.ts.map +1 -1
  28. package/dist/sync/pull.js +55 -4
  29. package/dist/sync/pull.js.map +1 -1
  30. package/dist/sync/push.d.ts +4 -1
  31. package/dist/sync/push.d.ts.map +1 -1
  32. package/dist/sync/push.js +22 -8
  33. package/dist/sync/push.js.map +1 -1
  34. package/dist/sync/types.d.ts +22 -0
  35. package/dist/sync/types.d.ts.map +1 -1
  36. package/dist/writers/entry.d.ts +95 -0
  37. package/dist/writers/entry.d.ts.map +1 -0
  38. package/dist/writers/entry.js +90 -0
  39. package/dist/writers/entry.js.map +1 -0
  40. package/dist/writers/index.d.ts +17 -0
  41. package/dist/writers/index.d.ts.map +1 -0
  42. package/dist/writers/index.js +14 -0
  43. package/dist/writers/index.js.map +1 -0
  44. package/dist/writers/roster.d.ts +152 -0
  45. package/dist/writers/roster.d.ts.map +1 -0
  46. package/dist/writers/roster.js +365 -0
  47. package/dist/writers/roster.js.map +1 -0
  48. package/dist/writers/store.d.ts +71 -0
  49. package/dist/writers/store.d.ts.map +1 -0
  50. package/dist/writers/store.js +55 -0
  51. package/dist/writers/store.js.map +1 -0
  52. package/package.json +10 -4
@@ -9,15 +9,18 @@ import type { ResolveConflict, SyncStore, WasSyncPort } from './types.js';
9
9
  * @param options.port {WasSyncPort}
10
10
  * @param options.store {SyncStore}
11
11
  * @param [options.resolveConflict] {ResolveConflict} mutable-collection policy
12
+ * @param [options.writerId] {string} this writer's attribution label, sent
13
+ * as `Writer-Id` on every write and delete; absent sends none
12
14
  * @param [options.signal] {AbortSignal}
13
15
  * @returns {Promise<{ pushed: number; conflictsResolved: number }>} dirty rows
14
16
  * processed this cycle, and how many invoked the LWW resolver (a positive
15
17
  * count means the caller should rerun so a local-wins re-push settles)
16
18
  */
17
- export declare function runPush({ port, store, resolveConflict, signal }: {
19
+ export declare function runPush({ port, store, resolveConflict, writerId, signal }: {
18
20
  port: WasSyncPort;
19
21
  store: SyncStore;
20
22
  resolveConflict?: ResolveConflict;
23
+ writerId?: string;
21
24
  signal?: AbortSignal;
22
25
  }): Promise<{
23
26
  pushed: number;
@@ -1 +1 @@
1
- {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/sync/push.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAQ,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AA2L/E;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,eAAe,EACf,MAAM,EACP,EAAE;IACD,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,EAAE,SAAS,CAAA;IAChB,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAwBzD"}
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/sync/push.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAQ,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAqM/E;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,OAAO,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,eAAe,EACf,QAAQ,EACR,MAAM,EACP,EAAE;IACD,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,EAAE,SAAS,CAAA;IAChB,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBzD"}
package/dist/sync/push.js CHANGED
@@ -24,6 +24,13 @@
24
24
  * folding a `putMeta` diff into this loop would add an untested code path with
25
25
  * no collection to exercise it. The `WasSyncPort.putMeta` capability stays
26
26
  * optional on the port for the driver that needs it.
27
+ *
28
+ * Every content write and delete carries the engine's injected `writerId`, when
29
+ * one was injected, as the `Writer-Id` header (the WAS writer-attribution
30
+ * label). The label is minted app-side; this loop never mints, persists, or
31
+ * derives one. The server's declare-or-clear rule means a write without it
32
+ * clears the stored label, so an engine run without a `writerId` attributes
33
+ * nothing rather than leaving a previous writer's label in place.
27
34
  */
28
35
  import { isSyncConflictError, isSyncNotFoundError } from '@interop/was-client/sync';
29
36
  /**
@@ -46,12 +53,13 @@ import { isSyncConflictError, isSyncNotFoundError } from '@interop/was-client/sy
46
53
  * absent/tombstone -> deletion wins, adopt the tombstone and delete the
47
54
  * projection (a later re-add re-encrypts to a fresh id, so nothing is blocked).
48
55
  */
49
- async function pushUpsert({ port, store, row, resolveConflict }) {
56
+ async function pushUpsert({ port, store, row, resolveConflict, writerId }) {
50
57
  try {
51
58
  const ack = await port.putContent({
52
59
  id: row.id,
53
60
  data: row.data ?? null,
54
- ...(row.version > 0 ? { ifMatch: row.etag } : { ifNoneMatch: true })
61
+ ...(row.version > 0 ? { ifMatch: row.etag } : { ifNoneMatch: true }),
62
+ ...(writerId !== undefined && { writerId })
55
63
  });
56
64
  await store.markPushed({
57
65
  id: row.id,
@@ -100,12 +108,13 @@ async function pushUpsert({ port, store, row, resolveConflict }) {
100
108
  * on a `412` so the caller can re-read and retry. Any other error propagates to
101
109
  * the engine's backoff.
102
110
  */
103
- async function tryDelete({ port, store, id, ifMatch, revision }) {
111
+ async function tryDelete({ port, store, id, ifMatch, revision, writerId }) {
104
112
  const revisionAck = revision !== undefined ? { revision } : {};
105
113
  try {
106
114
  const ack = await port.deleteContent({
107
115
  id,
108
- ...(ifMatch !== undefined && { ifMatch })
116
+ ...(ifMatch !== undefined && { ifMatch }),
117
+ ...(writerId !== undefined && { writerId })
109
118
  });
110
119
  await store.markDeletedPushed({
111
120
  id,
@@ -136,7 +145,7 @@ async function tryDelete({ port, store, id, ifMatch, revision }) {
136
145
  * refreshes its `version` / `etag` via the dirty-deleted-vs-live rule, so the
137
146
  * retry's `If-Match` becomes current).
138
147
  */
139
- async function pushDelete({ port, store, row }) {
148
+ async function pushDelete({ port, store, row, writerId }) {
140
149
  const revisionAck = row.revision !== undefined ? { revision: row.revision } : {};
141
150
  const firstIfMatch = row.version > 0 ? row.etag : undefined;
142
151
  if (await tryDelete({
@@ -144,6 +153,7 @@ async function pushDelete({ port, store, row }) {
144
153
  store,
145
154
  id: row.id,
146
155
  ifMatch: firstIfMatch,
156
+ writerId,
147
157
  ...revisionAck
148
158
  })) {
149
159
  return;
@@ -162,6 +172,7 @@ async function pushDelete({ port, store, row }) {
162
172
  store,
163
173
  id: row.id,
164
174
  ifMatch: master.etag,
175
+ writerId,
165
176
  ...revisionAck
166
177
  });
167
178
  }
@@ -175,12 +186,14 @@ async function pushDelete({ port, store, row }) {
175
186
  * @param options.port {WasSyncPort}
176
187
  * @param options.store {SyncStore}
177
188
  * @param [options.resolveConflict] {ResolveConflict} mutable-collection policy
189
+ * @param [options.writerId] {string} this writer's attribution label, sent
190
+ * as `Writer-Id` on every write and delete; absent sends none
178
191
  * @param [options.signal] {AbortSignal}
179
192
  * @returns {Promise<{ pushed: number; conflictsResolved: number }>} dirty rows
180
193
  * processed this cycle, and how many invoked the LWW resolver (a positive
181
194
  * count means the caller should rerun so a local-wins re-push settles)
182
195
  */
183
- export async function runPush({ port, store, resolveConflict, signal }) {
196
+ export async function runPush({ port, store, resolveConflict, writerId, signal }) {
184
197
  const rows = await store.getDirtyRows();
185
198
  let pushed = 0;
186
199
  let conflictsResolved = 0;
@@ -189,14 +202,15 @@ export async function runPush({ port, store, resolveConflict, signal }) {
189
202
  break;
190
203
  }
191
204
  if (row.deleted) {
192
- await pushDelete({ port, store, row });
205
+ await pushDelete({ port, store, row, writerId });
193
206
  }
194
207
  else {
195
208
  const { conflictResolved } = await pushUpsert({
196
209
  port,
197
210
  store,
198
211
  row,
199
- resolveConflict
212
+ resolveConflict,
213
+ writerId
200
214
  });
201
215
  if (conflictResolved) {
202
216
  conflictsResolved += 1;
@@ -1 +1 @@
1
- {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/sync/push.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/sync/push.ts"],"names":[],"mappings":"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"}
@@ -165,6 +165,28 @@ export interface SyncStore {
165
165
  latest: MasterState | null;
166
166
  projection: ProjectionAction;
167
167
  }): Promise<void>;
168
+ /**
169
+ * Which of these feed revisions does the replica already hold? Answers with
170
+ * the ids whose local row records exactly that `etag` as its validator (the
171
+ * one a push ack or an earlier pull recorded). The pull loop asks it only
172
+ * about live feed documents carrying the engine's own `writerId`, and skips
173
+ * the decrypt of each confirmed one, applying it with a `none` projection
174
+ * (see `pull.ts`). The label is host-visible plaintext, so the skip also
175
+ * requires the `etag` match. That match limits the skip to a revision the
176
+ * replica already records. It does not authenticate the label or the
177
+ * envelope, since the `etag` is host-supplied too. A host that replays an
178
+ * older envelope under the current `etag` gets the local projection left
179
+ * as it was, where a decrypt would have rolled it back.
180
+ *
181
+ * Optional: a store that omits it turns echo suppression off, and every
182
+ * echo is decrypted as before.
183
+ */
184
+ heldRevisions?(options: {
185
+ documents: {
186
+ id: string;
187
+ etag: string;
188
+ }[];
189
+ }): Promise<Set<string>>;
168
190
  /**
169
191
  * Replaces the body of a pending row (dirty, never-acked, live -- no feed
170
192
  * existence) with a re-minted envelope, re-keying the row from `id` to
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The version-1 roster entry. A type alias rather than an interface, so it
3
+ * is assignable to was-client's `JsonObject`.
4
+ */
5
+ export type RegisteredWriterEntry = {
6
+ version: 1;
7
+ /**
8
+ * The writer's `writerId`, verbatim. The resource id is a hash of it, so a
9
+ * reader takes the id from here.
10
+ */
11
+ writerId: string;
12
+ /**
13
+ * An enrolled wallet client's own signing-key multibase, the key the
14
+ * client-labels record and `listAccountClients` rows are keyed by. Absent
15
+ * for a non-wallet writer.
16
+ */
17
+ signingKeyMultibase?: string;
18
+ /**
19
+ * A coarse platform self-description, or the user's rename of it.
20
+ */
21
+ label: string;
22
+ /**
23
+ * ISO 8601 UTC timestamp of the writer's latest liveness touch.
24
+ */
25
+ lastSeen: string;
26
+ /**
27
+ * `false` once a sweep found `lastSeen` past the inactivity TTL. The writer's
28
+ * next touch sets it back to `true`.
29
+ */
30
+ active: boolean;
31
+ };
32
+ /**
33
+ * The expiry and liveness policy of the roster. Every replica that sweeps
34
+ * must use the same values, or two sweepers disagree about which entries are
35
+ * orphans.
36
+ */
37
+ export interface RegisteredWriterPolicy {
38
+ /**
39
+ * How long an entry may go untouched before a sweep marks it inactive.
40
+ */
41
+ inactiveAfterMs: number;
42
+ /**
43
+ * The entry count past which a sweep (or an enrollment) deletes the
44
+ * oldest-`lastSeen` entries.
45
+ */
46
+ maxEntries: number;
47
+ /**
48
+ * The least time between two liveness touches by the same writer.
49
+ */
50
+ touchIntervalMs: number;
51
+ }
52
+ /**
53
+ * The default policy: inactive after 90 days, at most 64 entries, touched at
54
+ * most once a day. A wallet that overrides any of these passes the same
55
+ * override on every replica.
56
+ */
57
+ export declare const REGISTERED_WRITER_POLICY: RegisteredWriterPolicy;
58
+ /**
59
+ * The domain label of the resource id hash.
60
+ */
61
+ export declare const REGISTERED_WRITER_ID_LABEL = "registered-writers/v1";
62
+ /**
63
+ * The resource id of one writer's entry under one account: the first 16
64
+ * bytes of `sha256(utf8('registered-writers/v1|' + accountDid + '|' +
65
+ * writerId))`, formatted as an EDV document id.
66
+ *
67
+ * The id is deterministic, so a registration and a touch are idempotent
68
+ * upserts. It hides the `writerId` from the storage host and does not link
69
+ * one `writerId` across accounts.
70
+ *
71
+ * @param options {object}
72
+ * @param options.accountDid {string} the account whose Space holds the roster
73
+ * @param options.writerId {string}
74
+ * @returns {string}
75
+ */
76
+ export declare function registeredWriterResourceId({ accountDid, writerId }: {
77
+ accountDid: string;
78
+ writerId: string;
79
+ }): string;
80
+ /**
81
+ * Resolves a partial policy override against the defaults.
82
+ *
83
+ * @param [policy] {Partial<RegisteredWriterPolicy>}
84
+ * @returns {RegisteredWriterPolicy}
85
+ */
86
+ export declare function registeredWriterPolicy(policy?: Partial<RegisteredWriterPolicy>): RegisteredWriterPolicy;
87
+ /**
88
+ * Reads a stored body as a roster entry, or `undefined` when it is not one.
89
+ * A malformed entry is skipped by every reader, and never repaired.
90
+ *
91
+ * @param body {unknown}
92
+ * @returns {RegisteredWriterEntry | undefined}
93
+ */
94
+ export declare function parseRegisteredWriterEntry(body: unknown): RegisteredWriterEntry | undefined;
95
+ //# sourceMappingURL=entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/writers/entry.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,CAAC,CAAA;IACV;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,eAAe,EAAE,MAAM,CAAA;IACvB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,eAAe,EAAE,MAAM,CAAA;CACxB;AAID;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,sBAItC,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AAEjE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,UAAU,EACV,QAAQ,EACT,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB,GAAG,MAAM,CAKT;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,GACvC,sBAAsB,CAExB;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,OAAO,GACZ,qBAAqB,GAAG,SAAS,CA2BnC"}
@@ -0,0 +1,90 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The writer roster's entry: its version-1 shape, the shape check every
6
+ * reader runs, the resource id an entry lives at, and the expiry policy both
7
+ * wallets sweep under.
8
+ *
9
+ * An entry is advisory display data. Its writer asserts every field about
10
+ * itself, and nothing checks them beyond shape. `signingKeyMultibase` links a
11
+ * wallet writer to its enrolled client only when the verified did:webvh
12
+ * document lists that key, and no field is an input to any authorization.
13
+ */
14
+ import { sha256 } from '@noble/hashes/sha2.js';
15
+ import { edvIdFromBytes } from '@interop/was-client/edv/cipher';
16
+ const DAY_MS = 24 * 60 * 60 * 1000;
17
+ /**
18
+ * The default policy: inactive after 90 days, at most 64 entries, touched at
19
+ * most once a day. A wallet that overrides any of these passes the same
20
+ * override on every replica.
21
+ */
22
+ export const REGISTERED_WRITER_POLICY = {
23
+ inactiveAfterMs: 90 * DAY_MS,
24
+ maxEntries: 64,
25
+ touchIntervalMs: DAY_MS
26
+ };
27
+ /**
28
+ * The domain label of the resource id hash.
29
+ */
30
+ export const REGISTERED_WRITER_ID_LABEL = 'registered-writers/v1';
31
+ /**
32
+ * The resource id of one writer's entry under one account: the first 16
33
+ * bytes of `sha256(utf8('registered-writers/v1|' + accountDid + '|' +
34
+ * writerId))`, formatted as an EDV document id.
35
+ *
36
+ * The id is deterministic, so a registration and a touch are idempotent
37
+ * upserts. It hides the `writerId` from the storage host and does not link
38
+ * one `writerId` across accounts.
39
+ *
40
+ * @param options {object}
41
+ * @param options.accountDid {string} the account whose Space holds the roster
42
+ * @param options.writerId {string}
43
+ * @returns {string}
44
+ */
45
+ export function registeredWriterResourceId({ accountDid, writerId }) {
46
+ const input = new TextEncoder().encode(`${REGISTERED_WRITER_ID_LABEL}|${accountDid}|${writerId}`);
47
+ return edvIdFromBytes(sha256(input).subarray(0, 16));
48
+ }
49
+ /**
50
+ * Resolves a partial policy override against the defaults.
51
+ *
52
+ * @param [policy] {Partial<RegisteredWriterPolicy>}
53
+ * @returns {RegisteredWriterPolicy}
54
+ */
55
+ export function registeredWriterPolicy(policy) {
56
+ return { ...REGISTERED_WRITER_POLICY, ...policy };
57
+ }
58
+ /**
59
+ * Reads a stored body as a roster entry, or `undefined` when it is not one.
60
+ * A malformed entry is skipped by every reader, and never repaired.
61
+ *
62
+ * @param body {unknown}
63
+ * @returns {RegisteredWriterEntry | undefined}
64
+ */
65
+ export function parseRegisteredWriterEntry(body) {
66
+ if (body === null || typeof body !== 'object') {
67
+ return undefined;
68
+ }
69
+ const { version, writerId, signingKeyMultibase, label, lastSeen, active } = body;
70
+ if (version !== 1 ||
71
+ typeof writerId !== 'string' ||
72
+ writerId === '' ||
73
+ typeof label !== 'string' ||
74
+ typeof lastSeen !== 'string' ||
75
+ Number.isNaN(Date.parse(lastSeen)) ||
76
+ typeof active !== 'boolean' ||
77
+ (signingKeyMultibase !== undefined &&
78
+ typeof signingKeyMultibase !== 'string')) {
79
+ return undefined;
80
+ }
81
+ return {
82
+ version: 1,
83
+ writerId,
84
+ ...(signingKeyMultibase !== undefined && { signingKeyMultibase }),
85
+ label,
86
+ lastSeen,
87
+ active
88
+ };
89
+ }
90
+ //# sourceMappingURL=entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.js","sourceRoot":"","sources":["../../src/writers/entry.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;GASG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAuD/D,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAA2B;IAC9D,eAAe,EAAE,EAAE,GAAG,MAAM;IAC5B,UAAU,EAAE,EAAE;IACd,eAAe,EAAE,MAAM;CACxB,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAA;AAEjE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,0BAA0B,CAAC,EACzC,UAAU,EACV,QAAQ,EAIT;IACC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CACpC,GAAG,0BAA0B,IAAI,UAAU,IAAI,QAAQ,EAAE,CAC1D,CAAA;IACD,OAAO,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAwC;IAExC,OAAO,EAAE,GAAG,wBAAwB,EAAE,GAAG,MAAM,EAAE,CAAA;AACnD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CACxC,IAAa;IAEb,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9C,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,GACvE,IAA+B,CAAA;IACjC,IACE,OAAO,KAAK,CAAC;QACb,OAAO,QAAQ,KAAK,QAAQ;QAC5B,QAAQ,KAAK,EAAE;QACf,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAO,QAAQ,KAAK,QAAQ;QAC5B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAClC,OAAO,MAAM,KAAK,SAAS;QAC3B,CAAC,mBAAmB,KAAK,SAAS;YAChC,OAAO,mBAAmB,KAAK,QAAQ,CAAC,EAC1C,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO;QACL,OAAO,EAAE,CAAC;QACV,QAAQ;QACR,GAAG,CAAC,mBAAmB,KAAK,SAAS,IAAI,EAAE,mBAAmB,EAAE,CAAC;QACjE,KAAK;QACL,QAAQ;QACR,MAAM;KACP,CAAA;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The writer roster: the `registered-writers` collection's entry shape, its
6
+ * resource id, the storage seam and WAS adapter, lazy registration with the
7
+ * throttled liveness touch, the sweep-on-read with two-phase expiry, and the
8
+ * join a history view resolves a revision's `writerId` through. Every field
9
+ * is advisory display data, and none is an input to any decision.
10
+ */
11
+ export { REGISTERED_WRITER_ID_LABEL, REGISTERED_WRITER_POLICY, parseRegisteredWriterEntry, registeredWriterPolicy, registeredWriterResourceId } from './entry.js';
12
+ export type { RegisteredWriterEntry, RegisteredWriterPolicy } from './entry.js';
13
+ export { wasRegisteredWritersStore } from './store.js';
14
+ export type { RegisteredWritersStore, StoredRegisteredWriter } from './store.js';
15
+ export { registerWriterOnSecondSession, renameRegisteredWriter, resolveRegisteredWriter, sweepRegisteredWriters } from './roster.js';
16
+ export type { RegisteredWritersSweep, ResolvedWriter, WriterRegistrationOutcome, WriterFirstSeenRecord, WriterFirstSeenStore } from './roster.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/writers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,0BAA0B,EAC1B,sBAAsB,EACtB,0BAA0B,EAC3B,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAC/E,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AACtD,YAAY,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAChF,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,sBAAsB,EACtB,cAAc,EACd,yBAAyB,EACzB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,aAAa,CAAA"}
@@ -0,0 +1,14 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The writer roster: the `registered-writers` collection's entry shape, its
6
+ * resource id, the storage seam and WAS adapter, lazy registration with the
7
+ * throttled liveness touch, the sweep-on-read with two-phase expiry, and the
8
+ * join a history view resolves a revision's `writerId` through. Every field
9
+ * is advisory display data, and none is an input to any decision.
10
+ */
11
+ export { REGISTERED_WRITER_ID_LABEL, REGISTERED_WRITER_POLICY, parseRegisteredWriterEntry, registeredWriterPolicy, registeredWriterResourceId } from './entry.js';
12
+ export { wasRegisteredWritersStore } from './store.js';
13
+ export { registerWriterOnSecondSession, renameRegisteredWriter, resolveRegisteredWriter, sweepRegisteredWriters } from './roster.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/writers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,0BAA0B,EAC1B,sBAAsB,EACtB,0BAA0B,EAC3B,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAEtD,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACvB,MAAM,aAAa,CAAA"}
@@ -0,0 +1,152 @@
1
+ import type { RegisteredWriterEntry, RegisteredWriterPolicy } from './entry.js';
2
+ import type { RegisteredWritersStore } from './store.js';
3
+ /**
4
+ * The writer first-seen record, kept client-local: the account and
5
+ * `writerId` this client's writer first appeared under, and when it last
6
+ * touched its roster entry. One slot, so a writer that moves to another
7
+ * account starts over.
8
+ */
9
+ export interface WriterFirstSeenRecord {
10
+ accountDid: string;
11
+ writerId: string;
12
+ /**
13
+ * ISO 8601 UTC start of the session the record was written in.
14
+ */
15
+ firstSessionAt: string;
16
+ /**
17
+ * ISO 8601 UTC time of the latest successful registration or touch.
18
+ */
19
+ lastTouchedAt?: string;
20
+ }
21
+ /**
22
+ * The client-local seam for the writer first-seen record. Where it lives is
23
+ * the wallet's choice. A forget of this client's local state clears it along with
24
+ * the `writerId`.
25
+ */
26
+ export interface WriterFirstSeenStore {
27
+ get(): Promise<unknown>;
28
+ put(record: WriterFirstSeenRecord): Promise<void>;
29
+ }
30
+ /**
31
+ * What {@link registerWriterOnSecondSession} did.
32
+ *
33
+ * - `deferred` -- the writer has not yet been encountered in a second session
34
+ * under this account; the first-seen record is (re)written and the roster
35
+ * is untouched.
36
+ * - `fresh` -- the latest touch is within the touch interval; no I/O.
37
+ * - `registered` -- a new entry was written (or a malformed one replaced).
38
+ * - `touched` -- the existing entry's `lastSeen` was advanced.
39
+ * - `raced` -- every attempt lost a conditional write; the next call retries.
40
+ */
41
+ export type WriterRegistrationOutcome = 'deferred' | 'fresh' | 'registered' | 'touched' | 'raced';
42
+ /**
43
+ * Registers this client's writer in the roster once it is encountered in a
44
+ * second session under the account, and touches its `lastSeen` at most once
45
+ * per touch interval after that. Call it on the wallet's connect or sync path;
46
+ * calls within the interval cost no I/O.
47
+ *
48
+ * A new entry takes `label`. A touch keeps the stored label, so a rename
49
+ * stands, and refreshes `signingKeyMultibase` when one is given. Registering
50
+ * a new entry first makes room under the entry cap, deleting the
51
+ * oldest-`lastSeen` entries.
52
+ *
53
+ * @param options {object}
54
+ * @param options.store {RegisteredWritersStore}
55
+ * @param options.local {WriterFirstSeenStore}
56
+ * @param options.accountDid {string} the account whose Space holds the roster
57
+ * @param options.writerId {string} this client's writer
58
+ * @param options.label {string} the coarse platform self-description a new
59
+ * entry takes
60
+ * @param options.sessionStartedAt {Date} when the current session began; a
61
+ * first-seen record written at or after it is this session's own
62
+ * @param [options.signingKeyMultibase] {string} an enrolled wallet client's
63
+ * own signing-key multibase
64
+ * @param [options.now] {Date}
65
+ * @param [options.policy] {Partial<RegisteredWriterPolicy>}
66
+ * @returns {Promise<WriterRegistrationOutcome>}
67
+ */
68
+ export declare function registerWriterOnSecondSession({ store, local, accountDid, writerId, label, sessionStartedAt, signingKeyMultibase, now, policy }: {
69
+ store: RegisteredWritersStore;
70
+ local: WriterFirstSeenStore;
71
+ accountDid: string;
72
+ writerId: string;
73
+ label: string;
74
+ sessionStartedAt: Date;
75
+ signingKeyMultibase?: string;
76
+ now?: Date;
77
+ policy?: Partial<RegisteredWriterPolicy>;
78
+ }): Promise<WriterRegistrationOutcome>;
79
+ /**
80
+ * Renames this client's writer. The rename lands only on a registered entry;
81
+ * before registration there is nothing to rename, and the call returns
82
+ * `false`.
83
+ *
84
+ * @param options {object}
85
+ * @param options.store {RegisteredWritersStore}
86
+ * @param options.accountDid {string}
87
+ * @param options.writerId {string}
88
+ * @param options.label {string} the new label; blank is refused
89
+ * @returns {Promise<boolean>} whether an entry was renamed
90
+ */
91
+ export declare function renameRegisteredWriter({ store, accountDid, writerId, label }: {
92
+ store: RegisteredWritersStore;
93
+ accountDid: string;
94
+ writerId: string;
95
+ label: string;
96
+ }): Promise<boolean>;
97
+ /**
98
+ * The outcome of a sweep: the entries left standing, and what it changed.
99
+ */
100
+ export interface RegisteredWritersSweep {
101
+ entries: RegisteredWriterEntry[];
102
+ markedInactive: string[];
103
+ deleted: string[];
104
+ }
105
+ /**
106
+ * Reads the roster and sweeps it: the sweep-on-read every history view runs.
107
+ * Safe to run from any replica, and concurrently.
108
+ *
109
+ * @param options {object}
110
+ * @param options.store {RegisteredWritersStore}
111
+ * @param options.accountDid {string} the account whose Space holds the roster
112
+ * @param [options.now] {Date}
113
+ * @param [options.policy] {Partial<RegisteredWriterPolicy>}
114
+ * @returns {Promise<RegisteredWritersSweep>}
115
+ */
116
+ export declare function sweepRegisteredWriters({ store, accountDid, now, policy }: {
117
+ store: RegisteredWritersStore;
118
+ accountDid: string;
119
+ now?: Date;
120
+ policy?: Partial<RegisteredWriterPolicy>;
121
+ }): Promise<RegisteredWritersSweep>;
122
+ /**
123
+ * How a history view renders one revision's writer. `undefined` from
124
+ * {@link resolveRegisteredWriter} means no entry: the view renders its
125
+ * fallback label ("another session").
126
+ */
127
+ export interface ResolvedWriter {
128
+ label: string;
129
+ active: boolean;
130
+ /**
131
+ * Present only when the entry's `signingKeyMultibase` is an enrolled
132
+ * client's key in the verified account document. The view may then link
133
+ * the revision to that client's row. Absent, the label stands unlinked.
134
+ */
135
+ clientSigningKeyMultibase?: string;
136
+ }
137
+ /**
138
+ * Resolves a revision's `writerId` against the swept roster.
139
+ *
140
+ * @param options {object}
141
+ * @param options.writerId {string}
142
+ * @param options.entries {RegisteredWriterEntry[]} a sweep's `entries`
143
+ * @param options.enrolledSigningKeys {ReadonlySet<string>} the signing-key
144
+ * multibases of the enrolled clients in the verified account document
145
+ * @returns {ResolvedWriter | undefined}
146
+ */
147
+ export declare function resolveRegisteredWriter({ writerId, entries, enrolledSigningKeys }: {
148
+ writerId: string;
149
+ entries: RegisteredWriterEntry[];
150
+ enrolledSigningKeys: ReadonlySet<string>;
151
+ }): ResolvedWriter | undefined;
152
+ //# sourceMappingURL=roster.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roster.d.ts","sourceRoot":"","sources":["../../src/writers/roster.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAC/E,OAAO,KAAK,EAAE,sBAAsB,EAA0B,MAAM,YAAY,CAAA;AAEhF;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAA;IACtB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IACvB,GAAG,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClD;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,yBAAyB,GACnC,UAAU,GAAG,OAAO,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,CAAA;AAyI3D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,6BAA6B,CAAC,EAClD,KAAK,EACL,KAAK,EACL,UAAU,EACV,QAAQ,EACR,KAAK,EACL,gBAAgB,EAChB,mBAAmB,EACnB,GAAgB,EAChB,MAAM,EACP,EAAE;IACD,KAAK,EAAE,sBAAsB,CAAA;IAC7B,KAAK,EAAE,oBAAoB,CAAA;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,gBAAgB,EAAE,IAAI,CAAA;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;CACzC,GAAG,OAAO,CAAC,yBAAyB,CAAC,CA0ErC;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAAC,EAC3C,KAAK,EACL,UAAU,EACV,QAAQ,EACR,KAAK,EACN,EAAE;IACD,KAAK,EAAE,sBAAsB,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd,GAAG,OAAO,CAAC,OAAO,CAAC,CAoBnB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAiGD;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAAC,EAC3C,KAAK,EACL,UAAU,EACV,GAAgB,EAChB,MAAM,EACP,EAAE;IACD,KAAK,EAAE,sBAAsB,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;CACzC,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAUlC;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAA;CACnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,OAAO,EACP,mBAAmB,EACpB,EAAE;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACzC,GAAG,cAAc,GAAG,SAAS,CAa7B"}