@intentproof/sdk 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,12 @@
1
1
  ## **Logs narrate; IntentProof gives you proof.**
2
2
 
3
- Turn your function calls into **verifiable** execution records designed to be reconciled.
3
+ **IntentProof** is **auditable execution records** for actions that must be defensible—**intent** tied to what actually ran.
4
+
5
+ **Wrap** the calls that matter; each invocation emits one **verifiable** **`ExecutionEvent`**, structured so intent and outcome can be **reconciled** with reality—not only observed.
6
+
4
7
  Observability captures what happened. **IntentProof** tells you whether it matched what was **meant to happen**.
5
8
 
6
- Every wrapped call emits one **`ExecutionEvent`** containing:
9
+ Every **`ExecutionEvent`** contains:
7
10
 
8
11
  - **`intent`**: what this invocation was meant to prove
9
12
  - **`action`**: the stable operation id for this step
@@ -26,14 +29,27 @@ They don't tell you:
26
29
 
27
30
  It records intent alongside execution so systems can be verified, not just observed.
28
31
 
32
+ ### Picture this:
33
+
34
+ It's 4:47 on a Friday. A customer insists the critical action never happened. Support sees scattered traces; engineering sees green checks; finance asks for **one** clean chain: what was **supposed** to occur, what **did** occur, and whether the outcome is **complete**.
35
+
36
+ Ordinary telemetry shows that *something ran*. It rarely ships an **auditable story** you can hand to someone who doesn't read your codebase. **IntentProof** exists for when the question stops being "what was logged?" and starts being **"prove it."**
37
+
29
38
  ## Requirements
30
39
 
31
40
  - **Node.js** 22 or newer
32
41
 
33
42
  ## Install
34
43
 
44
+ **Package:** `@intentproof/sdk`.
45
+
46
+ - [npm — `@intentproof/sdk`](https://www.npmjs.com/package/@intentproof/sdk)
47
+ - [GitHub Releases — IntentProof Node SDK](https://github.com/IntentProof/intentproof-sdk-node/releases)
48
+
49
+ Pin the **version** you want from npm or from GitHub Releases. Replace **`x.y.z`** below with that version.
50
+
35
51
  ```bash
36
- npm install @intentproof/sdk
52
+ npm install @intentproof/sdk@x.y.z
37
53
  ```
38
54
 
39
55
  ## Quick start
@@ -49,80 +65,81 @@ const refund = client.wrap(
49
65
 
50
66
  Each refund call emits one **`ExecutionEvent`** with the **`intent`** and **`action`** you chose, the **`inputs`** and **`output`** (or **`error`** + **`status: "error"`**), and timing fields—an execution record you can inspect, export, or verify later.
51
67
 
52
- ## `IntentProofClient` API
68
+ ## Reference
53
69
 
70
+ Detailed tables for the client API, emitted events, configuration, and related exports.
54
71
 
55
- | Member | Description |
56
- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
57
- | **`constructor(config?)`** | Creates a client. Default exporters: a single **`MemoryExporter`** if you omit **`config.exporters`**. |
58
- | **`configure(config)`** | Re-applies **`IntentProofConfig`** fields (exporters, error hook, defaults, stack policy). |
59
- | **`wrap(options, fn)`** | Returns a function that records one **`ExecutionEvent`** per call (sync or async). **`options`** must satisfy **`assertWrapOptionsShape`** (`intent` / `action` non-empty strings, etc.). |
60
- | **`flush()`** | Awaits **`flush()`** on every **`Exporter`** that implements it, in parallel. |
61
- | **`shutdown()`** | For each **`Exporter`**, awaits **`shutdown()`** if implemented, otherwise **`flush()`** if implemented. |
62
- | **`getCorrelationId()`** | Returns the correlation ID from **`AsyncLocalStorage`**, if any. |
63
- | **`withCorrelation(fn)`** | Runs **`fn`** with a **fresh UUID** as correlation ID for nested wraps. |
64
- | **`withCorrelation(id, fn)`** | Runs **`fn`** with **`id`** trimmed; blank / whitespace-only **`id`** falls back to a UUID. |
72
+ ### `IntentProofClient` API
65
73
 
74
+ | Member | Description |
75
+ | ------ | ----------- |
76
+ | **`constructor(config?)`** | Creates a client. Default exporters: a single **`MemoryExporter`** if you omit **`config.exporters`**. |
77
+ | **`configure(config)`** | Re-applies **`IntentProofConfig`** fields (exporters, error hook, defaults, stack policy). |
78
+ | **`wrap(options, fn)`** | Returns a function that records one **`ExecutionEvent`** per call (sync or async). **`options`** must satisfy **`assertWrapOptionsShape`** (`intent` / `action` non-empty strings, etc.). |
79
+ | **`flush()`** | Awaits **`flush()`** on every **`Exporter`** that implements it, in parallel. |
80
+ | **`shutdown()`** | For each **`Exporter`**, awaits **`shutdown()`** if implemented, otherwise **`flush()`** if implemented. |
81
+ | **`getCorrelationId()`** | Returns the correlation ID from **`AsyncLocalStorage`**, if any. |
82
+ | **`withCorrelation(fn)`** | Runs **`fn`** with a **fresh UUID** as correlation ID for nested wraps. |
83
+ | **`withCorrelation(id, fn)`** | Runs **`fn`** with **`id`** trimmed; blank / whitespace-only **`id`** falls back to a UUID. |
66
84
 
67
- ### Module-level helpers (same module as the client)
85
+ #### Module-level helpers (same module as the client)
68
86
 
69
87
  These use the same async correlation store as **`IntentProofClient`** instances:
70
88
 
89
+ | Export | Description |
90
+ | ------ | ----------- |
91
+ | **`createIntentProofClient(config?)`** | New isolated client (tests, workers, multi-tenant). |
92
+ | **`getIntentProofClient()`** | Lazy singleton used by **`client`**. |
93
+ | **`client`** | Default singleton instance. |
94
+ | **`getCorrelationId()`** | Same behavior as the instance method. |
95
+ | **`runWithCorrelationId(id, fn)`** | Requires a **non-empty** correlation ID after trim; throws if invalid. |
96
+ | **`assertCorrelationId(id)`** | Runtime assertion for correlation ID shape. |
97
+ | **`assertWrapOptionsShape(options)`** | Runtime validation for **`WrapOptions`**. |
98
+
99
+ ### `ExecutionEvent` fields
100
+
101
+ | Field | Description |
102
+ | ----- | ----------- |
103
+ | **`id`** | Unique event id (UUID). |
104
+ | **`correlationId`** | Request or trace correlation ID when present—usually from context or **`WrapOptions`**. |
105
+ | **`intent`** | Human-readable label for what this invocation is meant to prove (outcome, policy goal, or domain). |
106
+ | **`action`** | Stable operation id for this step (often dotted or namespaced). |
107
+ | **`inputs`** | JSON-safe snapshot of call arguments (default) or **`captureInput`** result. |
108
+ | **`output`** | JSON-safe return value or **`captureOutput`** result on success. When **`status`** is **`"error"`**, set only if **`captureError`** returned a value. |
109
+ | **`error`** | On failure: **`name`**, **`message`**, and optional **`stack`** (see **`includeErrorStack`**). |
110
+ | **`status`** | **`"ok"`** if the wrapped call completed normally; **`"error"`** if it threw. |
111
+ | **`startedAt`** | Start time (ISO 8601). |
112
+ | **`completedAt`** | Completion time (ISO 8601). |
113
+ | **`durationMs`** | Wall time between start and completion, in milliseconds. |
114
+ | **`attributes`** | Optional plain record (string / number / boolean values only), merged from client defaults and wrap options. |
115
+
116
+ ### `WrapOptions` and `IntentProofConfig`
117
+
118
+ #### `WrapOptions` (passed to **`wrap`**)
119
+
120
+ | Field | Description |
121
+ | ----- | ----------- |
122
+ | **`intent`**, **`action`** | Required, non-empty after trim. |
123
+ | **`correlationId`** | Optional; when set, non-empty after trim. Otherwise the active correlation ID from context is used, if any. |
124
+ | **`attributes`** | Per-invocation dimensions merged over **`defaultAttributes`**. |
125
+ | **`captureInput`**, **`captureOutput`**, **`captureError`** | Optional hooks to replace default **`snapshot`** behavior for inputs, success output, or error-side extra **`output`**. |
126
+ | **`includeErrorStack`** | When `false`, omit **`error.stack`** for this wrap (overrides client default). |
127
+ | **`maxDepth`**, **`maxKeys`**, **`redactKeys`**, **`maxStringLength`** | Forwarded to **`snapshot`** for inputs and outputs (see **`SerializeOptions`** in types). |
128
+
129
+ #### `IntentProofConfig` (constructor / **`configure`**)
130
+
131
+ | Field | Description |
132
+ | ----- | ----------- |
133
+ | **`exporters`** | Ordered list of **`Exporter`** instances; each receives every **`ExecutionEvent`**. |
134
+ | **`onExporterError`** | Called when any exporter’s **`export()`** throws or returns a rejected promise. Defaults to **`console.error`**. |
135
+ | **`defaultAttributes`** | Merged into every event’s **`attributes`** (wrap-specific attributes win on key collision). |
136
+ | **`includeErrorStack`** | Default `true`; set `false` in production if stacks must not leave the trust zone. |
137
+
138
+ ### Related exports
71
139
 
72
- | Export | Description |
73
- | -------------------------------------- | ---------------------------------------------------------------------- |
74
- | **`createIntentProofClient(config?)`** | New isolated client (tests, workers, multi-tenant). |
75
- | **`getIntentProofClient()`** | Lazy singleton used by **`client`**. |
76
- | **`client`** | Default singleton instance. |
77
- | **`getCorrelationId()`** | Same behavior as the instance method. |
78
- | **`runWithCorrelationId(id, fn)`** | Requires a **non-empty** correlation ID after trim; throws if invalid. |
79
- | **`assertCorrelationId(id)`** | Runtime assertion for correlation ID shape. |
80
- | **`assertWrapOptionsShape(options)`** | Runtime validation for **`WrapOptions`**. |
81
-
82
-
83
- ## `ExecutionEvent` fields
84
-
85
- | Field | Description |
86
- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
87
- | **`id`** | Unique event id (UUID). |
88
- | **`correlationId`** | Request or trace correlation ID when present—usually from context or **`WrapOptions`**. |
89
- | **`intent`** | Human-readable label for what this invocation is meant to prove (outcome, policy goal, or domain). |
90
- | **`action`** | Stable operation id for this step (often dotted or namespaced). |
91
- | **`inputs`** | JSON-safe snapshot of call arguments (default) or **`captureInput`** result. |
92
- | **`output`** | JSON-safe return value or **`captureOutput`** result on success. When **`status`** is **`"error"`**, set only if **`captureError`** returned a value. |
93
- | **`error`** | On failure: **`name`**, **`message`**, and optional **`stack`** (see **`includeErrorStack`**). |
94
- | **`status`** | **`"ok"`** if the wrapped call completed normally; **`"error"`** if it threw. |
95
- | **`startedAt`** | Start time (ISO 8601). |
96
- | **`completedAt`** | Completion time (ISO 8601). |
97
- | **`durationMs`** | Wall time between start and completion, in milliseconds. |
98
- | **`attributes`** | Optional plain record (string / number / boolean values only), merged from client defaults and wrap options. |
99
-
100
-
101
- ## `WrapOptions` and `IntentProofConfig`
102
-
103
- ### `WrapOptions` (passed to **`wrap`**)
104
-
105
-
106
- | Field | Description |
107
- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
108
- | **`intent`**, **`action`** | Required, non-empty after trim. |
109
- | **`correlationId`** | Optional; when set, non-empty after trim. Otherwise the active correlation ID from context is used, if any. |
110
- | **`attributes`** | Per-invocation dimensions merged over **`defaultAttributes`**. |
111
- | **`captureInput`**, **`captureOutput`**, **`captureError`** | Optional hooks to replace default **`snapshot`** behavior for inputs, success output, or error-side extra **`output`**. |
112
- | **`includeErrorStack`** | When `false`, omit **`error.stack`** for this wrap (overrides client default). |
113
- | **`maxDepth`**, **`maxKeys`**, **`redactKeys`**, **`maxStringLength`** | Forwarded to **`snapshot`** for inputs and outputs (see **`SerializeOptions`** in types). |
114
-
115
-
116
- ### `IntentProofConfig` (constructor / **`configure`**)
117
-
118
-
119
- | Field | Description |
120
- | ----------------------- | ---------------------------------------------------------------------------------------------------------------- |
121
- | **`exporters`** | Ordered list of **`Exporter`** instances; each receives every **`ExecutionEvent`**. |
122
- | **`onExporterError`** | Called when any exporter’s **`export()`** throws or returns a rejected promise. Defaults to **`console.error`**. |
123
- | **`defaultAttributes`** | Merged into every event’s **`attributes`** (wrap-specific attributes win on key collision). |
124
- | **`includeErrorStack`** | Default `true`; set `false` in production if stacks must not leave the trust zone. |
125
-
140
+ - **`MemoryExporter`**, **`HttpExporter`**, **`BoundedQueueExporter`** — Delivery implementations; each implements **`Exporter`**.
141
+ - **`snapshot`** Same JSON-safe serializer the client uses internally, if you build custom tooling.
142
+ - **`VERSION`** Package version string injected at build time.
126
143
 
127
144
  ---
128
145
 
@@ -134,6 +151,8 @@ Support approves **order `ORD-1042`**. Your service creates the **Stripe refund*
134
151
 
135
152
  **`captureInput`** / **`captureOutput`** trim each record to the fields you want in proof (refund id, amounts, message id)—not full vendor payloads.
136
153
 
154
+ JSON on the wire uses **camelCase**; TypeScript **`WrapOptions`** use the same camelCase names (e.g. **`captureInput`**).
155
+
137
156
  ```ts
138
157
  const createRefund = client.wrap(
139
158
  {
@@ -320,7 +339,7 @@ try {
320
339
 
321
340
  ### 3 — Proof delivery over HTTP (same **`ExecutionEvent`** shape)
322
341
 
323
- **`HttpExporter`** POSTs the same **`ExecutionEvent`** your verifiers see in memory—here alongside **`MemoryExporter`** so tests can assert the wire without a real collector. The request uses **`credentials: "omit"`**; the body is **`{ intentproof: "1", event: … }`** (see exporter implementation). For authenticated collectors, pass **`headers`** (e.g. **`Authorization`**, API keys) — see [Security](#security).
342
+ **`HttpExporter`** POSTs the same **`ExecutionEvent`** your verifiers see in memory—here alongside **`MemoryExporter`** so tests can assert the wire without a real collector. The request omits ambient credentials; the body is **`{ "intentproof": "1", "event": … }`** (see exporter implementation). For authenticated collectors, pass **`headers`** (e.g. **`Authorization`**, API keys) — see [Security](#security).
324
343
 
325
344
  ```ts
326
345
  const runProbe = client.wrap({ intent: "HTTP test", action: "test.http" }, () => 42);
@@ -341,30 +360,53 @@ runProbe();
341
360
 
342
361
  ## Security
343
362
 
363
+ For **vulnerability reporting**, use this repository’s [**Security**](https://github.com/IntentProof/intentproof-sdk-node/security) tab (private advisories).
364
+
344
365
  Every **`ExecutionEvent`** you emit is data you may ship off-process. Treat them like audit-grade execution records: they can include PII, secrets, stack traces, and business identifiers depending on your **`snapshot`** / **`capture*`** hooks.
345
366
 
346
367
  - **Minimize payload:** Use **`redactKeys`**, **`maxDepth`** / **`maxKeys`** / **`maxStringLength`**, and narrow **`captureInput`** / **`captureOutput`** / **`captureError`** so proof records contain only what verifiers need.
347
368
  - **Stacks:** Set **`includeErrorStack: false`** on the client (or per wrap) when traces must not leave your trust zone.
348
369
  - **HTTP ingest:** Keep collector **`url`** and any redirect behavior under **trusted configuration** (avoid SSRF if URLs were ever influenced by untrusted input). Prefer **HTTPS** and **short-lived credentials** end-to-end.
349
370
  - **`HttpExporter` auth:** Pass credentials in **`headers`** (for example **`Authorization: Bearer …`**, **`x-api-key`**, or whatever your collector expects). The SDK does **not** log header values; use short-lived tokens and scope them to ingest only.
350
- - **Browser vs server:** This package targets **Node**; if you wrap code in a browser, treat the ingest endpoint and headers as you would any cross-origin credential (CORS, CSP, token storage policies are your app’s responsibility).
371
+ - **Runtime surface:** This package targets **Node**; if you wrap code in a browser, treat the ingest endpoint and headers as you would any cross-origin credential (CORS, CSP, token storage policies are your app’s responsibility).
351
372
  - **Delivery semantics:** Exporter failures invoke **`onExporterError`** and do **not** roll back the wrapped function’s side effects—design compensating controls if you need strict “delivered exactly once” guarantees.
352
373
 
353
374
  Custom **`body`** serializers: if **`body(event)`** throws, **`HttpExporter`** notifies **`onError`** and falls back to the same **JSON envelope** path as the default serializer (full event, then a partial envelope, then a minimal `eventSerializeFailed` payload) so **`export()`** still completes and **`fetch`** runs when possible.
354
375
 
355
376
  ---
356
377
 
357
- ## Related exports
378
+ ## Canonical specification (`intentproof-spec`)
358
379
 
359
- - **`MemoryExporter`**, **`HttpExporter`**, **`BoundedQueueExporter`** Delivery implementations; each implements **`Exporter`**.
360
- - **`snapshot`** — Same JSON-safe serializer the client uses internally, if you build custom tooling.
361
- - **`VERSION`** Package version string injected at build time.
380
+ **Shared pins and terminology** (`INTENTPROOF_SPEC_ROOT`, **`intentproofSpecCommit`**, script names): **[`intentproof-spec` CONTRIBUTING Terminology](https://github.com/IntentProof/intentproof-spec/blob/main/CONTRIBUTING.md#terminology-shared-with-sdk-repos)**.
381
+
382
+ Schemas, golden oracles, and the **Vitest conformance oracle** live in the **[IntentProof specification repository (`intentproof-spec`)](https://github.com/IntentProof/intentproof-spec)**.
383
+
384
+ - **Version pin:** **`intentproofSpecVersion`** and **`intentproofSpecCommit`** in the root **`package.json`** and **`packages/sdk/package.json`** match **`spec.json`** and the spec **`HEAD`** checkout; **`scripts/check-sdk-spec-pin.sh`** enforces this before conformance.
385
+
386
+ - **CI:** every push/PR checks out this SDK plus **`intentproof-spec`** and runs **`scripts/spec-conformance.sh`** (pin check + full oracle; see `.github/workflows/ci.yml`). The **`sdk`** job sets **`INTENTPROOF_SPEC_ROOT`** so **`packages/sdk`** Vitest also imports the spec **`sdk_test_harness`**—golden **`execution_event_cases.jsonl`** oracle plus a **`MemoryExporter`** **`validateExecutionEvent`** smoke (`spec_conformance.integration.test.ts`).
387
+ - **Local:** clone `intentproof-spec` **next to** this repository (`../intentproof-spec`), then:
388
+
389
+ ```bash
390
+ npm run spec:conformance
391
+ ```
392
+
393
+ Or set `INTENTPROOF_SPEC_ROOT` to your spec checkout and run `bash scripts/spec-conformance.sh`.
394
+
395
+ - **Generated fingerprint metadata:** schema codegen writes **`packages/sdk/src/generated/spec_fingerprint.json`** (spec version, generator version, per-schema SHA-256, aggregate hash). Validate/update generated artifacts with:
396
+
397
+ ```bash
398
+ bash scripts/verify-generated-types.sh
399
+ ```
400
+
401
+ - **No handwritten model types:** **`scripts/check-no-handwritten-model-types.sh`** delegates to the shared **`intentproof-spec`** checker. It is wired into **`npm run ci`**, CI, and release, and fails if schema model/type declarations appear outside **`packages/sdk/src/generated`** or if the bridge aliases in **`packages/sdk/src/types.ts`** stop mapping to generated types.
402
+
403
+ ---
362
404
 
363
- ## Monorepo development
405
+ ## Project development
364
406
 
365
- This repository is an npm workspace; the publishable package is [`packages/sdk`](packages/sdk).
407
+ Contributing and shared **`intentproof-spec`** terminology: **[`CONTRIBUTING.md`](CONTRIBUTING.md)**.
366
408
 
367
- Requires **Node.js** 22 or newer (see `.nvmrc` and workspace `engines`).
409
+ Layout: **npm workspace** (`package.json` **`workspaces`**, publishable package [`packages/sdk`](packages/sdk)). Requires **Node.js** 22 or newer (see `.nvmrc` and workspace **`engines`**). Release history: [`CHANGELOG.md`](CHANGELOG.md).
368
410
 
369
411
  ```bash
370
412
  npm ci
@@ -373,4 +415,4 @@ npm run ci
373
415
 
374
416
  ## License
375
417
 
376
- Apache-2.0 (see `LICENSE` at the repository root and in the published npm package).
418
+ Apache-2.0 (see `LICENSE` at the repository root and in the published npm package when released).