@anvailabs/sandhi 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/sandhi.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ // Type entry point for @anvai-labs/sandhi. Re-exports the napi-generated types and augments
2
+ // `ByteStream` with the `Symbol.asyncIterator` that `sandhi.js` installs at runtime (ADR-0047 D10
3
+ // step 3c), so `for await (const chunk of stream)` type-checks.
4
+
5
+ export * from "./index";
6
+ export * from "./contracts";
7
+ import type { ProviderErrorV1 } from "./contracts";
8
+
9
+ /** Provider-boundary error; `payload` is the parsed ProviderErrorV1 (TD-0008 P2/C). */
10
+ export declare class SandhiProviderError extends Error {
11
+ payload: ProviderErrorV1;
12
+ code: string;
13
+ httpStatus: number | null;
14
+ retryable: boolean;
15
+ requestId: string | null;
16
+ details: Record<string, unknown>;
17
+ }
18
+ declare module "./index" {
19
+ interface TypedEventStream {
20
+ [Symbol.asyncIterator](): AsyncIterator<string>;
21
+ }
22
+ }
package/sandhi.js ADDED
@@ -0,0 +1,113 @@
1
+ // Sandhi Node entry point (ADR-0047 D10 step 3c).
2
+ //
3
+ // Re-exports the napi-generated binding (`index.js`) and adds the one thing the Rust addon can't
4
+ // declare itself: a `Symbol.asyncIterator` on `ByteStream`, so a streaming transport call is usable
5
+ // with `for await`. `ByteStream.read()` resolves to the next chunk, or `null` when exhausted.
6
+ //
7
+ // This file is hand-written and stable; `index.js` / `index.d.ts` are regenerated by `napi build`.
8
+ // The named re-exports below are literal (not `module.exports = native`) so ESM consumers can do
9
+ // `import { complete } from "@anvai-labs/sandhi"` — cjs-module-lexer only sees literal assignments.
10
+ // Keep this list in sync with `index.js` when the addon gains a new export.
11
+
12
+ const native = require("./index.js");
13
+
14
+ /**
15
+ * Provider-boundary error (parity with the Python binding's SandhiProviderError,
16
+ * TD-0008 P2/C). `payload` is the parsed ProviderErrorV1; `code` / `httpStatus` /
17
+ * `retryable` / `requestId` are conveniences so consumers branch on typed fields
18
+ * instead of string-sniffing arbitrary Error messages.
19
+ */
20
+ class SandhiProviderError extends Error {
21
+ constructor(payload, message) {
22
+ super(message);
23
+ this.name = "SandhiProviderError";
24
+ this.payload = payload;
25
+ this.code = payload.code;
26
+ this.httpStatus = payload.http_status ?? null;
27
+ this.retryable = Boolean(payload.retryable);
28
+ this.requestId = payload.request_id ?? null;
29
+ this.details = payload.details ?? {};
30
+ }
31
+ }
32
+
33
+ // The native layer throws plain Errors whose message is the ProviderErrorV1 JSON.
34
+ // Rewrap into the typed class when the shape matches; anything else passes through
35
+ // (binding-internal failures stay ordinary Errors — the distinction is the point).
36
+ function toSandhiError(error) {
37
+ if (error instanceof SandhiProviderError) return error;
38
+ const message = error && typeof error.message === "string" ? error.message : null;
39
+ if (message && message.startsWith("{")) {
40
+ try {
41
+ const payload = JSON.parse(message);
42
+ if (payload && typeof payload.code === "string") {
43
+ return new SandhiProviderError(payload, message);
44
+ }
45
+ } catch (_) {
46
+ /* not a typed payload — fall through */
47
+ }
48
+ }
49
+ return error;
50
+ }
51
+
52
+ if (native.TypedEventStream && !native.TypedEventStream.prototype[Symbol.asyncIterator]) {
53
+ native.TypedEventStream.prototype[Symbol.asyncIterator] = function asyncIterator() {
54
+ const stream = this;
55
+ return {
56
+ async next() {
57
+ let event;
58
+ try {
59
+ event = await stream.read();
60
+ } catch (error) {
61
+ throw toSandhiError(error);
62
+ }
63
+ return event === null || event === undefined
64
+ ? { done: true, value: undefined }
65
+ : { done: false, value: event };
66
+ },
67
+ };
68
+ };
69
+ }
70
+
71
+ if (native.TypedProvider && !native.TypedProvider.prototype.__sandhiTypedErrors) {
72
+ const rawCompleteJson = native.TypedProvider.prototype.completeJson;
73
+ if (typeof rawCompleteJson === "function") {
74
+ // Variadic forward: the wrapper adds typed-error rewiring only — every call argument
75
+ // (incl. the optional per-call wireHeadersJson, TD-0022 D1) reaches the native method.
76
+ native.TypedProvider.prototype.completeJson = async function completeJson(
77
+ ...args
78
+ ) {
79
+ try {
80
+ return await rawCompleteJson.call(this, ...args);
81
+ } catch (error) {
82
+ throw toSandhiError(error);
83
+ }
84
+ };
85
+ }
86
+ // streamJson: the SETUP call can itself fail (upstream 401/timeout at connect)
87
+ // before any iterator exists — those must be instanceof-checkable too, not just
88
+ // the per-event read() errors (TD-0021 P1 / G22 close-out).
89
+ const rawStreamJson = native.TypedProvider.prototype.streamJson;
90
+ if (typeof rawStreamJson === "function") {
91
+ native.TypedProvider.prototype.streamJson = function streamJson(...args) {
92
+ try {
93
+ return rawStreamJson.call(this, ...args);
94
+ } catch (error) {
95
+ throw toSandhiError(error);
96
+ }
97
+ };
98
+ }
99
+ native.TypedProvider.prototype.__sandhiTypedErrors = true;
100
+ }
101
+
102
+ module.exports.wireContractVersion = native.wireContractVersion;
103
+ module.exports.chatContractVersion = native.chatContractVersion;
104
+ module.exports.parseUsage = native.parseUsage;
105
+ module.exports.providerDescriptorJson = native.providerDescriptorJson;
106
+ module.exports.providerSpecJson = native.providerSpecJson;
107
+ module.exports.chatContractSchemaJson = native.chatContractSchemaJson;
108
+ module.exports.providerModelsJson = native.providerModelsJson;
109
+ module.exports.SandhiProviderError = SandhiProviderError;
110
+ module.exports.Gateway = native.Gateway;
111
+ module.exports.ProviderRuntime = native.ProviderRuntime;
112
+ module.exports.TypedProvider = native.TypedProvider;
113
+ module.exports.TypedEventStream = native.TypedEventStream;
Binary file