experimental-a2 0.14.1 → 0.15.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 197b719: Add `idempotentId(...parts: string[])` to derive stable UUIDv8 event IDs from
8
+ ordered strings in server and browser code. Existing IDs and automatic ID
9
+ generation are unchanged.
10
+
3
11
  ## 0.14.1
4
12
 
5
13
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -2,4 +2,8 @@ import { a as ContractEvent, c as PresenceDefs, d as PresenceSnapshot, f as Redu
2
2
  import { i as Event } from "./store-D_yhNdPz.js";
3
3
  import { i as NonRetriableError, n as A2ErrorCode, r as A2ServerErrorCode, t as A2Error } from "./errors-DvhSXnxk.js";
4
4
  import { i as A2Telemetry, n as A2SpanHandle, r as A2SpanName, t as A2AttributeValue } from "./telemetry-CpeclqB2.js";
5
- export { type A2AttributeValue, A2Error, type A2ErrorCode, type A2ServerErrorCode, type A2SpanHandle, type A2SpanName, type A2Telemetry, type AppendInput, type Contract, type ContractEvent, type Event, type EventBatchFactory, type EventDefs, NonRetriableError, type PresenceDefs, type PresenceMap, type PresencePatch, type PresenceSnapshot, type Reducer, type ReducerBuilder, type ReducerOptions, type StandardSchemaV1, contract };
5
+ //#region src/idempotent-id.d.ts
6
+ declare function idempotentId(...parts: string[]): Promise<string>;
7
+ //#endregion
8
+ export { type A2AttributeValue, A2Error, type A2ErrorCode, type A2ServerErrorCode, type A2SpanHandle, type A2SpanName, type A2Telemetry, type AppendInput, type Contract, type ContractEvent, type Event, type EventBatchFactory, type EventDefs, NonRetriableError, type PresenceDefs, type PresenceMap, type PresencePatch, type PresenceSnapshot, type Reducer, type ReducerBuilder, type ReducerOptions, type StandardSchemaV1, contract, idempotentId };
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/idempotent-id.ts"],"mappings":";;;;;iBAAsB,gBAAgB,kBAAkB"}
package/dist/index.js CHANGED
@@ -1,3 +1,15 @@
1
1
  import { t as contract } from "./contract-CKRg_E4q.js";
2
2
  import { n as NonRetriableError, t as A2Error } from "./errors-DCk6ch5n.js";
3
- export { A2Error, NonRetriableError, contract };
3
+ //#region src/idempotent-id.ts
4
+ async function idempotentId(...parts) {
5
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(parts)));
6
+ const bytes = new Uint8Array(digest).slice(0, 16);
7
+ bytes[6] = bytes[6] & 15 | 128;
8
+ bytes[8] = bytes[8] & 63 | 128;
9
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
10
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
11
+ }
12
+ //#endregion
13
+ export { A2Error, NonRetriableError, contract, idempotentId };
14
+
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/idempotent-id.ts"],"sourcesContent":["export async function idempotentId(...parts: string[]): Promise<string> {\n const digest = await crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(JSON.stringify(parts)),\n )\n const bytes = new Uint8Array(digest).slice(0, 16)\n bytes[6] = (bytes[6]! & 0x0f) | 0x80\n bytes[8] = (bytes[8]! & 0x3f) | 0x80\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`\n}\n"],"mappings":";;;AAAA,eAAsB,aAAa,GAAG,OAAkC;CACtE,MAAM,SAAS,MAAM,OAAO,OAAO,OACjC,WACA,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,UAAU,KAAK,CAAC,CAChD;CACA,MAAM,QAAQ,IAAI,WAAW,MAAM,CAAC,CAAC,MAAM,GAAG,EAAE;CAChD,MAAM,KAAM,MAAM,KAAM,KAAQ;CAChC,MAAM,KAAM,MAAM,KAAM,KAAQ;CAChC,MAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;CAC7E,OAAO,GAAG,IAAI,MAAM,GAAG,CAAC,EAAE,GAAG,IAAI,MAAM,GAAG,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE,EAAE,GAAG,IAAI,MAAM,IAAI,EAAE;AAC7G"}
@@ -63,6 +63,31 @@ The shared base for A2 failures, discriminated by `code`. The session
63
63
  client exposes its `A2ClientError` subclass. See
64
64
  [Errors](/reference/errors).
65
65
 
66
+ ### `idempotentId(...parts)`
67
+
68
+ `idempotentId(...parts: string[]): Promise<string>` derives a stable UUIDv8
69
+ from an ordered tuple of strings. It runs on the server and in the browser.
70
+
71
+ ```ts shared/request-id.ts
72
+ import { idempotentId } from 'experimental-a2'
73
+
74
+ export const id = await idempotentId('orders', 'shop-7', 'request-42')
75
+ ```
76
+
77
+ Pass the result as an event's `id` when retries should converge on the same
78
+ event. The same tuple produces the same ID across runtimes and A2 versions.
79
+ String boundaries, order, and case matter; strings are not normalized.
80
+ Empty strings and an empty tuple are valid inputs.
81
+
82
+ Every argument has the same role. A prefix such as `'orders'` is a namespace
83
+ by convention. Include enough context to distinguish operations across
84
+ contracts and sessions, since event IDs are globally unique within a store.
85
+
86
+ The helper performs no I/O. A2 preserves explicit IDs and keeps its automatic
87
+ ID generation unchanged. Changing an existing application's ID recipe changes
88
+ retry identity, so retries of previously committed requests need to retain
89
+ their original IDs.
90
+
66
91
  ## `experimental-a2/server`
67
92
 
68
93
  ### `handler(entry)`
@@ -22,7 +22,7 @@
22
22
  "@vercel/sandbox": "^3.0.0",
23
23
  "ai": "^7.0.58",
24
24
  "codemirror": "^6.0.2",
25
- "experimental-a2": "0.14.1",
25
+ "experimental-a2": "0.15.0",
26
26
  "ioredis": "^5.9.0",
27
27
  "next": "^16.3.0",
28
28
  "pg": "^8.16.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "experimental-a2",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Durable sync and reactions for things with a lifecycle: one event log, derived state, and live client per session.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,11 @@
1
+ export async function idempotentId(...parts: string[]): Promise<string> {
2
+ const digest = await crypto.subtle.digest(
3
+ 'SHA-256',
4
+ new TextEncoder().encode(JSON.stringify(parts)),
5
+ )
6
+ const bytes = new Uint8Array(digest).slice(0, 16)
7
+ bytes[6] = (bytes[6]! & 0x0f) | 0x80
8
+ bytes[8] = (bytes[8]! & 0x3f) | 0x80
9
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')
10
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`
11
+ }
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  export { contract } from './contract.ts'
11
+ export { idempotentId } from './idempotent-id.ts'
11
12
  export type {
12
13
  AppendInput,
13
14
  Contract,