@negotium/adapter-sdk 0.1.7 → 0.1.8
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 +4 -1
- package/dist/outbox.d.ts +51 -0
- package/dist/outbox.d.ts.map +1 -0
- package/dist/outbox.js +151 -0
- package/dist/outbox.js.map +10 -0
- package/dist/testkit.d.ts +14 -0
- package/dist/testkit.d.ts.map +1 -0
- package/dist/testkit.js +66 -0
- package/dist/testkit.js.map +11 -0
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @negotium/adapter-sdk
|
|
2
2
|
|
|
3
|
-
Versioned lifecycle contract shared by Negotium channel adapters. An API
|
|
3
|
+
Versioned lifecycle contract shared by Negotium channel adapters. An API v3 adapter definition has
|
|
4
4
|
a stable name, transcript projection capabilities, `start(options)`, and an idempotent `stop()`
|
|
5
5
|
handle.
|
|
6
6
|
|
|
@@ -25,3 +25,6 @@ export const exampleAdapter = defineNegotiumAdapter({
|
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
Requires Bun 1.2.15 or newer.
|
|
28
|
+
|
|
29
|
+
Durable delivery helpers are exported from `@negotium/adapter-sdk/outbox`. Runner-neutral adapter
|
|
30
|
+
contract assertions are exported from `@negotium/adapter-sdk/testkit`.
|
package/dist/outbox.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type OutboxAwaitable<T> = T | Promise<T>;
|
|
2
|
+
export interface DurableOutboxEntry {
|
|
3
|
+
attempts: number;
|
|
4
|
+
}
|
|
5
|
+
export interface DurableOutboxFailure {
|
|
6
|
+
message: string;
|
|
7
|
+
retryAfterMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface DurableOutboxRetry {
|
|
10
|
+
attempts: number;
|
|
11
|
+
nextTryAt: number;
|
|
12
|
+
error: string;
|
|
13
|
+
}
|
|
14
|
+
export interface DurableOutboxPolicy {
|
|
15
|
+
pollMs: number;
|
|
16
|
+
baseDelayMs: number;
|
|
17
|
+
maxDelayMs: number;
|
|
18
|
+
maxAttempts: number;
|
|
19
|
+
}
|
|
20
|
+
export interface DurableOutboxStore<Entry extends DurableOutboxEntry, Delivery> {
|
|
21
|
+
due(now: number): OutboxAwaitable<readonly Entry[]>;
|
|
22
|
+
hasPending(): OutboxAwaitable<boolean>;
|
|
23
|
+
acknowledge(entry: Entry, delivery: Delivery): OutboxAwaitable<void>;
|
|
24
|
+
discard(entry: Entry): OutboxAwaitable<void>;
|
|
25
|
+
retry(entry: Entry, retry: DurableOutboxRetry): OutboxAwaitable<void>;
|
|
26
|
+
deadLetter(entry: Entry, retry: Omit<DurableOutboxRetry, "nextTryAt">): OutboxAwaitable<void>;
|
|
27
|
+
}
|
|
28
|
+
export interface DurableOutboxWorkerOptions<Entry extends DurableOutboxEntry, Delivery, Timer = ReturnType<typeof setTimeout>> {
|
|
29
|
+
store: DurableOutboxStore<Entry, Delivery>;
|
|
30
|
+
deliver(entry: Entry): Promise<Delivery>;
|
|
31
|
+
classifyError(error: unknown): DurableOutboxFailure;
|
|
32
|
+
policy: DurableOutboxPolicy;
|
|
33
|
+
shouldDiscard?(entry: Entry): OutboxAwaitable<boolean>;
|
|
34
|
+
onEntryStart?(entry: Entry): void;
|
|
35
|
+
onEntryEnd?(entry: Entry): void;
|
|
36
|
+
onDeadLetter?(entry: Entry, retry: Omit<DurableOutboxRetry, "nextTryAt">): void;
|
|
37
|
+
onError?(error: unknown): void;
|
|
38
|
+
now?: () => number;
|
|
39
|
+
schedule?: (callback: () => void, delayMs: number) => Timer;
|
|
40
|
+
cancel?: (timer: Timer) => void;
|
|
41
|
+
}
|
|
42
|
+
export interface DurableOutboxWorker {
|
|
43
|
+
start(): void;
|
|
44
|
+
wake(): void;
|
|
45
|
+
flush(): Promise<void>;
|
|
46
|
+
stop(): void;
|
|
47
|
+
}
|
|
48
|
+
export declare function exponentialOutboxBackoff(attempts: number, policy: Pick<DurableOutboxPolicy, "baseDelayMs" | "maxDelayMs">): number;
|
|
49
|
+
/** Drive a durable store without coupling retry policy to a transport or database. */
|
|
50
|
+
export declare function createDurableOutboxWorker<Entry extends DurableOutboxEntry, Delivery, Timer = ReturnType<typeof setTimeout>>(options: DurableOutboxWorkerOptions<Entry, Delivery, Timer>): DurableOutboxWorker;
|
|
51
|
+
//# sourceMappingURL=outbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../src/outbox.ts"],"names":[],"mappings":"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"}
|
package/dist/outbox.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/outbox.ts
|
|
3
|
+
function exponentialOutboxBackoff(attempts, policy) {
|
|
4
|
+
return Math.min(policy.baseDelayMs * 2 ** Math.max(0, attempts - 1), policy.maxDelayMs);
|
|
5
|
+
}
|
|
6
|
+
function createDurableOutboxWorker(options) {
|
|
7
|
+
const now = options.now ?? Date.now;
|
|
8
|
+
const schedule = options.schedule ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
9
|
+
const cancel = options.cancel ?? ((timer2) => clearTimeout(timer2));
|
|
10
|
+
let timer;
|
|
11
|
+
let started = false;
|
|
12
|
+
let stopped = false;
|
|
13
|
+
let flushing = null;
|
|
14
|
+
let pendingAcknowledgement = null;
|
|
15
|
+
const reportError = (error) => {
|
|
16
|
+
try {
|
|
17
|
+
options.onError?.(error);
|
|
18
|
+
} catch {}
|
|
19
|
+
};
|
|
20
|
+
const scheduleFlush = () => {
|
|
21
|
+
if (!started || stopped || timer !== undefined)
|
|
22
|
+
return;
|
|
23
|
+
timer = schedule(() => {
|
|
24
|
+
timer = undefined;
|
|
25
|
+
flush();
|
|
26
|
+
}, options.policy.pollMs);
|
|
27
|
+
};
|
|
28
|
+
const performFlush = async () => {
|
|
29
|
+
let entries = [];
|
|
30
|
+
const released = new Set;
|
|
31
|
+
const release = (entry) => {
|
|
32
|
+
if (released.has(entry))
|
|
33
|
+
return;
|
|
34
|
+
released.add(entry);
|
|
35
|
+
options.onEntryEnd?.(entry);
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
if (pendingAcknowledgement) {
|
|
39
|
+
const pending = pendingAcknowledgement;
|
|
40
|
+
try {
|
|
41
|
+
await options.store.acknowledge(pending.entry, pending.delivery);
|
|
42
|
+
pendingAcknowledgement = null;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
reportError(error);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
entries = await options.store.due(now());
|
|
49
|
+
for (const entry of entries)
|
|
50
|
+
options.onEntryStart?.(entry);
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
if (stopped)
|
|
53
|
+
return;
|
|
54
|
+
if (await options.shouldDiscard?.(entry)) {
|
|
55
|
+
await options.store.discard(entry);
|
|
56
|
+
release(entry);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
let delivery;
|
|
60
|
+
try {
|
|
61
|
+
delivery = await options.deliver(entry);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (stopped)
|
|
64
|
+
return;
|
|
65
|
+
const failure = options.classifyError(error);
|
|
66
|
+
const attempts = entry.attempts + 1;
|
|
67
|
+
const retry = { attempts, error: failure.message };
|
|
68
|
+
if (attempts >= options.policy.maxAttempts) {
|
|
69
|
+
await options.store.deadLetter(entry, retry);
|
|
70
|
+
options.onDeadLetter?.(entry, retry);
|
|
71
|
+
} else {
|
|
72
|
+
const delay = failure.retryAfterMs ?? exponentialOutboxBackoff(attempts, options.policy);
|
|
73
|
+
await options.store.retry(entry, {
|
|
74
|
+
...retry,
|
|
75
|
+
nextTryAt: now() + Math.max(0, delay)
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
release(entry);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (stopped)
|
|
82
|
+
return;
|
|
83
|
+
pendingAcknowledgement = { entry, delivery };
|
|
84
|
+
try {
|
|
85
|
+
await options.store.acknowledge(entry, delivery);
|
|
86
|
+
pendingAcknowledgement = null;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
reportError(error);
|
|
89
|
+
release(entry);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
release(entry);
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (!stopped)
|
|
96
|
+
reportError(error);
|
|
97
|
+
} finally {
|
|
98
|
+
for (const entry of entries)
|
|
99
|
+
release(entry);
|
|
100
|
+
if (!stopped && started) {
|
|
101
|
+
let hasPending = pendingAcknowledgement !== null;
|
|
102
|
+
if (!hasPending) {
|
|
103
|
+
try {
|
|
104
|
+
hasPending = await options.store.hasPending();
|
|
105
|
+
} catch (error) {
|
|
106
|
+
reportError(error);
|
|
107
|
+
hasPending = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (hasPending)
|
|
111
|
+
scheduleFlush();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const flush = () => {
|
|
116
|
+
if (stopped)
|
|
117
|
+
return Promise.resolve();
|
|
118
|
+
if (flushing)
|
|
119
|
+
return flushing;
|
|
120
|
+
flushing = performFlush().finally(() => {
|
|
121
|
+
flushing = null;
|
|
122
|
+
});
|
|
123
|
+
return flushing;
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
start() {
|
|
127
|
+
if (started || stopped)
|
|
128
|
+
return;
|
|
129
|
+
started = true;
|
|
130
|
+
scheduleFlush();
|
|
131
|
+
},
|
|
132
|
+
wake() {
|
|
133
|
+
scheduleFlush();
|
|
134
|
+
},
|
|
135
|
+
flush,
|
|
136
|
+
stop() {
|
|
137
|
+
if (stopped)
|
|
138
|
+
return;
|
|
139
|
+
stopped = true;
|
|
140
|
+
if (timer !== undefined)
|
|
141
|
+
cancel(timer);
|
|
142
|
+
timer = undefined;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export {
|
|
147
|
+
exponentialOutboxBackoff,
|
|
148
|
+
createDurableOutboxWorker
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
//# debugId=E225B28CEEC5AEB564756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/outbox.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"export type OutboxAwaitable<T> = T | Promise<T>;\n\nexport interface DurableOutboxEntry {\n attempts: number;\n}\n\nexport interface DurableOutboxFailure {\n message: string;\n retryAfterMs?: number;\n}\n\nexport interface DurableOutboxRetry {\n attempts: number;\n nextTryAt: number;\n error: string;\n}\n\nexport interface DurableOutboxPolicy {\n pollMs: number;\n baseDelayMs: number;\n maxDelayMs: number;\n maxAttempts: number;\n}\n\nexport interface DurableOutboxStore<Entry extends DurableOutboxEntry, Delivery> {\n due(now: number): OutboxAwaitable<readonly Entry[]>;\n hasPending(): OutboxAwaitable<boolean>;\n acknowledge(entry: Entry, delivery: Delivery): OutboxAwaitable<void>;\n discard(entry: Entry): OutboxAwaitable<void>;\n retry(entry: Entry, retry: DurableOutboxRetry): OutboxAwaitable<void>;\n deadLetter(entry: Entry, retry: Omit<DurableOutboxRetry, \"nextTryAt\">): OutboxAwaitable<void>;\n}\n\nexport interface DurableOutboxWorkerOptions<\n Entry extends DurableOutboxEntry,\n Delivery,\n Timer = ReturnType<typeof setTimeout>,\n> {\n store: DurableOutboxStore<Entry, Delivery>;\n deliver(entry: Entry): Promise<Delivery>;\n classifyError(error: unknown): DurableOutboxFailure;\n policy: DurableOutboxPolicy;\n shouldDiscard?(entry: Entry): OutboxAwaitable<boolean>;\n onEntryStart?(entry: Entry): void;\n onEntryEnd?(entry: Entry): void;\n onDeadLetter?(entry: Entry, retry: Omit<DurableOutboxRetry, \"nextTryAt\">): void;\n onError?(error: unknown): void;\n now?: () => number;\n schedule?: (callback: () => void, delayMs: number) => Timer;\n cancel?: (timer: Timer) => void;\n}\n\nexport interface DurableOutboxWorker {\n start(): void;\n wake(): void;\n flush(): Promise<void>;\n stop(): void;\n}\n\nexport function exponentialOutboxBackoff(\n attempts: number,\n policy: Pick<DurableOutboxPolicy, \"baseDelayMs\" | \"maxDelayMs\">,\n): number {\n return Math.min(policy.baseDelayMs * 2 ** Math.max(0, attempts - 1), policy.maxDelayMs);\n}\n\n/** Drive a durable store without coupling retry policy to a transport or database. */\nexport function createDurableOutboxWorker<\n Entry extends DurableOutboxEntry,\n Delivery,\n Timer = ReturnType<typeof setTimeout>,\n>(options: DurableOutboxWorkerOptions<Entry, Delivery, Timer>): DurableOutboxWorker {\n const now = options.now ?? Date.now;\n const schedule =\n options.schedule ??\n ((callback: () => void, delayMs: number) => setTimeout(callback, delayMs) as Timer);\n const cancel =\n options.cancel ?? ((timer: Timer) => clearTimeout(timer as ReturnType<typeof setTimeout>));\n let timer: Timer | undefined;\n let started = false;\n let stopped = false;\n let flushing: Promise<void> | null = null;\n let pendingAcknowledgement: { entry: Entry; delivery: Delivery } | null = null;\n\n const reportError = (error: unknown): void => {\n try {\n options.onError?.(error);\n } catch {\n // An observer must not stop durable delivery recovery.\n }\n };\n\n const scheduleFlush = (): void => {\n if (!started || stopped || timer !== undefined) return;\n timer = schedule(() => {\n timer = undefined;\n void flush();\n }, options.policy.pollMs);\n };\n\n const performFlush = async (): Promise<void> => {\n let entries: readonly Entry[] = [];\n const released = new Set<Entry>();\n const release = (entry: Entry): void => {\n if (released.has(entry)) return;\n released.add(entry);\n options.onEntryEnd?.(entry);\n };\n try {\n if (pendingAcknowledgement) {\n const pending = pendingAcknowledgement;\n try {\n await options.store.acknowledge(pending.entry, pending.delivery);\n pendingAcknowledgement = null;\n } catch (error) {\n reportError(error);\n return;\n }\n }\n entries = await options.store.due(now());\n for (const entry of entries) options.onEntryStart?.(entry);\n for (const entry of entries) {\n if (stopped) return;\n if (await options.shouldDiscard?.(entry)) {\n await options.store.discard(entry);\n release(entry);\n continue;\n }\n let delivery: Delivery;\n try {\n delivery = await options.deliver(entry);\n } catch (error) {\n if (stopped) return;\n const failure = options.classifyError(error);\n const attempts = entry.attempts + 1;\n const retry = { attempts, error: failure.message };\n if (attempts >= options.policy.maxAttempts) {\n await options.store.deadLetter(entry, retry);\n options.onDeadLetter?.(entry, retry);\n } else {\n const delay =\n failure.retryAfterMs ?? exponentialOutboxBackoff(attempts, options.policy);\n await options.store.retry(entry, {\n ...retry,\n nextTryAt: now() + Math.max(0, delay),\n });\n }\n release(entry);\n continue;\n }\n if (stopped) return;\n pendingAcknowledgement = { entry, delivery };\n try {\n await options.store.acknowledge(entry, delivery);\n pendingAcknowledgement = null;\n } catch (error) {\n reportError(error);\n release(entry);\n return;\n }\n release(entry);\n }\n } catch (error) {\n if (!stopped) reportError(error);\n } finally {\n for (const entry of entries) release(entry);\n if (!stopped && started) {\n let hasPending = pendingAcknowledgement !== null;\n if (!hasPending) {\n try {\n hasPending = await options.store.hasPending();\n } catch (error) {\n reportError(error);\n hasPending = true;\n }\n }\n if (hasPending) scheduleFlush();\n }\n }\n };\n\n const flush = (): Promise<void> => {\n if (stopped) return Promise.resolve();\n if (flushing) return flushing;\n flushing = performFlush().finally(() => {\n flushing = null;\n });\n return flushing;\n };\n\n return {\n start(): void {\n if (started || stopped) return;\n started = true;\n scheduleFlush();\n },\n wake(): void {\n scheduleFlush();\n },\n flush,\n stop(): void {\n if (stopped) return;\n stopped = true;\n if (timer !== undefined) cancel(timer);\n timer = undefined;\n },\n };\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "E225B28CEEC5AEB564756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type NegotiumAdapterCapabilities, type NegotiumAdapterDefinition, type NegotiumAdapterHandle } from "./index";
|
|
2
|
+
export declare function assertNegotiumAdapterDefinition<Name extends string>(value: unknown, expectedName: Name): asserts value is NegotiumAdapterDefinition<Name, unknown, NegotiumAdapterHandle<Name>>;
|
|
3
|
+
export type NegotiumAdapterCapability = "localUserInput" | "topicManagement" | "externalPlacedTurn";
|
|
4
|
+
export declare function assertNegotiumAdapterCapability(definition: NegotiumAdapterDefinition<string, unknown, NegotiumAdapterHandle>, capability: NegotiumAdapterCapability, expected: boolean): void;
|
|
5
|
+
export declare function assertNegotiumAdapterHandle<Name extends string>(value: unknown, expectedName: Name): asserts value is NegotiumAdapterHandle<Name>;
|
|
6
|
+
export declare function assertAdapterStopIsIdempotent(handle: NegotiumAdapterHandle): Promise<void>;
|
|
7
|
+
export interface NegotiumAdapterContractOptions<Name extends string, Options, Handle extends NegotiumAdapterHandle<Name>> {
|
|
8
|
+
name: Name;
|
|
9
|
+
definition: NegotiumAdapterDefinition<Name, Options, Handle>;
|
|
10
|
+
capabilities: NegotiumAdapterCapabilities;
|
|
11
|
+
createHandle?: () => Handle | Promise<Handle>;
|
|
12
|
+
}
|
|
13
|
+
export declare function assertNegotiumAdapterContract<Name extends string, Options, Handle extends NegotiumAdapterHandle<Name>>(options: NegotiumAdapterContractOptions<Name, Options, Handle>): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=testkit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testkit.d.ts","sourceRoot":"","sources":["../src/testkit.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC3B,MAAM,SAAS,CAAC;AAEjB,wBAAgB,+BAA+B,CAAC,IAAI,SAAS,MAAM,EACjE,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,IAAI,GACjB,OAAO,CAAC,KAAK,IAAI,yBAAyB,CAAC,IAAI,EAAE,OAAO,EAAE,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAmCxF;AAED,MAAM,MAAM,yBAAyB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;AAEpG,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,yBAAyB,CAAC,MAAM,EAAE,OAAO,EAAE,qBAAqB,CAAC,EAC7E,UAAU,EAAE,yBAAyB,EACrC,QAAQ,EAAE,OAAO,GAChB,IAAI,CAMN;AAED,wBAAgB,2BAA2B,CAAC,IAAI,SAAS,MAAM,EAC7D,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,IAAI,GACjB,OAAO,CAAC,KAAK,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAW9C;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhG;AAED,MAAM,WAAW,8BAA8B,CAC7C,IAAI,SAAS,MAAM,EACnB,OAAO,EACP,MAAM,SAAS,qBAAqB,CAAC,IAAI,CAAC;IAE1C,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,yBAAyB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7D,YAAY,EAAE,2BAA2B,CAAC;IAC1C,YAAY,CAAC,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/C;AAED,wBAAsB,6BAA6B,CACjD,IAAI,SAAS,MAAM,EACnB,OAAO,EACP,MAAM,SAAS,qBAAqB,CAAC,IAAI,CAAC,EAC1C,OAAO,EAAE,8BAA8B,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAa/E"}
|
package/dist/testkit.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/index.ts
|
|
3
|
+
var NEGOTIUM_ADAPTER_API_VERSION = 3;
|
|
4
|
+
|
|
5
|
+
// src/testkit.ts
|
|
6
|
+
function assertNegotiumAdapterDefinition(value, expectedName) {
|
|
7
|
+
if (!value || typeof value !== "object") {
|
|
8
|
+
throw new TypeError(`adapter ${expectedName} did not export a definition`);
|
|
9
|
+
}
|
|
10
|
+
const candidate = value;
|
|
11
|
+
if (candidate.apiVersion !== NEGOTIUM_ADAPTER_API_VERSION) {
|
|
12
|
+
throw new TypeError(`adapter ${expectedName} has an incompatible API version`);
|
|
13
|
+
}
|
|
14
|
+
if (candidate.name !== expectedName || typeof candidate.start !== "function") {
|
|
15
|
+
throw new TypeError(`adapter definition must expose name=${expectedName} and start()`);
|
|
16
|
+
}
|
|
17
|
+
const projection = candidate.projection;
|
|
18
|
+
if (!projection || projection.transcript !== "full" && projection.transcript !== "live-only" || typeof projection.historyBackfill !== "boolean" || projection.externalAuthors !== "native" && projection.externalAuthors !== "relayed") {
|
|
19
|
+
throw new TypeError(`adapter ${expectedName} must declare transcript projection capabilities`);
|
|
20
|
+
}
|
|
21
|
+
const capabilities = candidate.capabilities;
|
|
22
|
+
if (!capabilities || typeof capabilities.localUserInput !== "boolean" || typeof capabilities.topicManagement !== "boolean" || typeof capabilities.externalPlacedTurn !== "boolean") {
|
|
23
|
+
throw new TypeError(`adapter ${expectedName} must declare behavioral capabilities`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function assertNegotiumAdapterCapability(definition, capability, expected) {
|
|
27
|
+
if (definition.capabilities[capability] !== expected) {
|
|
28
|
+
throw new TypeError(`adapter ${definition.name} capability ${capability} must be ${String(expected)}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function assertNegotiumAdapterHandle(value, expectedName) {
|
|
32
|
+
if (!value || typeof value !== "object") {
|
|
33
|
+
throw new TypeError(`adapter ${expectedName} did not return a handle`);
|
|
34
|
+
}
|
|
35
|
+
const candidate = value;
|
|
36
|
+
if (candidate.name !== expectedName) {
|
|
37
|
+
throw new TypeError(`adapter handle name must be ${expectedName}`);
|
|
38
|
+
}
|
|
39
|
+
if (typeof candidate.stop !== "function") {
|
|
40
|
+
throw new TypeError(`adapter ${expectedName} handle must expose stop()`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function assertAdapterStopIsIdempotent(handle) {
|
|
44
|
+
await handle.stop();
|
|
45
|
+
await handle.stop();
|
|
46
|
+
}
|
|
47
|
+
async function assertNegotiumAdapterContract(options) {
|
|
48
|
+
assertNegotiumAdapterDefinition(options.definition, options.name);
|
|
49
|
+
for (const capability of ["localUserInput", "topicManagement", "externalPlacedTurn"]) {
|
|
50
|
+
assertNegotiumAdapterCapability(options.definition, capability, options.capabilities[capability]);
|
|
51
|
+
}
|
|
52
|
+
if (!options.createHandle)
|
|
53
|
+
return;
|
|
54
|
+
const handle = await options.createHandle();
|
|
55
|
+
assertNegotiumAdapterHandle(handle, options.name);
|
|
56
|
+
await assertAdapterStopIsIdempotent(handle);
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
assertNegotiumAdapterHandle,
|
|
60
|
+
assertNegotiumAdapterDefinition,
|
|
61
|
+
assertNegotiumAdapterContract,
|
|
62
|
+
assertNegotiumAdapterCapability,
|
|
63
|
+
assertAdapterStopIsIdempotent
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
//# debugId=AF9B17C5EFFFA4EA64756E2164756E21
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/testkit.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/** Stable lifecycle contract implemented by every first-party channel adapter. */\n\nexport const NEGOTIUM_ADAPTER_API_VERSION = 3 as const;\n\nexport type Awaitable<T> = T | Promise<T>;\n\nexport interface NegotiumAdapterHandle<Name extends string = string> {\n /** Stable adapter key used in logs, configuration, and contract tests. */\n readonly name: Name;\n /** Stop accepting channel input and release every owned resource. Idempotent. */\n stop(): Awaitable<void>;\n}\n\n/** User-facing transcript semantics; transport gaps stay explicit metadata. */\nexport interface NegotiumAdapterProjectionCapabilities {\n /** Whether the surface can render a room timeline or only newly delivered events. */\n readonly transcript: \"full\" | \"live-only\";\n /** Whether pre-existing topic messages can populate the surface on binding. */\n readonly historyBackfill: boolean;\n /** How authors originating in another adapter are represented. */\n readonly externalAuthors: \"native\" | \"relayed\";\n}\n\n/** Optional behavioral surfaces are declared independently from lifecycle. */\nexport interface NegotiumAdapterCapabilities {\n /** Accepts a human message originating on this adapter. */\n readonly localUserInput: boolean;\n /** Exposes create/delete/reset/compact topic commands. */\n readonly topicManagement: boolean;\n /** Executes a turn placed by an external peer runtime. */\n readonly externalPlacedTurn: boolean;\n}\n\nexport interface NegotiumAdapterDefinition<\n Name extends string,\n Options,\n Handle extends NegotiumAdapterHandle<Name>,\n> {\n readonly apiVersion: typeof NEGOTIUM_ADAPTER_API_VERSION;\n readonly name: Name;\n readonly capabilities: NegotiumAdapterCapabilities;\n readonly projection: NegotiumAdapterProjectionCapabilities;\n start(options: Options): Awaitable<Handle>;\n}\n\nexport function defineNegotiumAdapter<\n const Name extends string,\n Options,\n Handle extends NegotiumAdapterHandle<Name>,\n>(\n definition: Omit<NegotiumAdapterDefinition<Name, Options, Handle>, \"apiVersion\">,\n): NegotiumAdapterDefinition<Name, Options, Handle> {\n return { apiVersion: NEGOTIUM_ADAPTER_API_VERSION, ...definition };\n}\n",
|
|
6
|
+
"import {\n NEGOTIUM_ADAPTER_API_VERSION,\n type NegotiumAdapterCapabilities,\n type NegotiumAdapterDefinition,\n type NegotiumAdapterHandle,\n} from \"./index\";\n\nexport function assertNegotiumAdapterDefinition<Name extends string>(\n value: unknown,\n expectedName: Name,\n): asserts value is NegotiumAdapterDefinition<Name, unknown, NegotiumAdapterHandle<Name>> {\n if (!value || typeof value !== \"object\") {\n throw new TypeError(`adapter ${expectedName} did not export a definition`);\n }\n const candidate = value as {\n apiVersion?: unknown;\n name?: unknown;\n capabilities?: unknown;\n projection?: unknown;\n start?: unknown;\n };\n if (candidate.apiVersion !== NEGOTIUM_ADAPTER_API_VERSION) {\n throw new TypeError(`adapter ${expectedName} has an incompatible API version`);\n }\n if (candidate.name !== expectedName || typeof candidate.start !== \"function\") {\n throw new TypeError(`adapter definition must expose name=${expectedName} and start()`);\n }\n const projection = candidate.projection as Record<string, unknown> | undefined;\n if (\n !projection ||\n (projection.transcript !== \"full\" && projection.transcript !== \"live-only\") ||\n typeof projection.historyBackfill !== \"boolean\" ||\n (projection.externalAuthors !== \"native\" && projection.externalAuthors !== \"relayed\")\n ) {\n throw new TypeError(`adapter ${expectedName} must declare transcript projection capabilities`);\n }\n const capabilities = candidate.capabilities as Record<string, unknown> | undefined;\n if (\n !capabilities ||\n typeof capabilities.localUserInput !== \"boolean\" ||\n typeof capabilities.topicManagement !== \"boolean\" ||\n typeof capabilities.externalPlacedTurn !== \"boolean\"\n ) {\n throw new TypeError(`adapter ${expectedName} must declare behavioral capabilities`);\n }\n}\n\nexport type NegotiumAdapterCapability = \"localUserInput\" | \"topicManagement\" | \"externalPlacedTurn\";\n\nexport function assertNegotiumAdapterCapability(\n definition: NegotiumAdapterDefinition<string, unknown, NegotiumAdapterHandle>,\n capability: NegotiumAdapterCapability,\n expected: boolean,\n): void {\n if (definition.capabilities[capability] !== expected) {\n throw new TypeError(\n `adapter ${definition.name} capability ${capability} must be ${String(expected)}`,\n );\n }\n}\n\nexport function assertNegotiumAdapterHandle<Name extends string>(\n value: unknown,\n expectedName: Name,\n): asserts value is NegotiumAdapterHandle<Name> {\n if (!value || typeof value !== \"object\") {\n throw new TypeError(`adapter ${expectedName} did not return a handle`);\n }\n const candidate = value as { name?: unknown; stop?: unknown };\n if (candidate.name !== expectedName) {\n throw new TypeError(`adapter handle name must be ${expectedName}`);\n }\n if (typeof candidate.stop !== \"function\") {\n throw new TypeError(`adapter ${expectedName} handle must expose stop()`);\n }\n}\n\nexport async function assertAdapterStopIsIdempotent(handle: NegotiumAdapterHandle): Promise<void> {\n await handle.stop();\n await handle.stop();\n}\n\nexport interface NegotiumAdapterContractOptions<\n Name extends string,\n Options,\n Handle extends NegotiumAdapterHandle<Name>,\n> {\n name: Name;\n definition: NegotiumAdapterDefinition<Name, Options, Handle>;\n capabilities: NegotiumAdapterCapabilities;\n createHandle?: () => Handle | Promise<Handle>;\n}\n\nexport async function assertNegotiumAdapterContract<\n Name extends string,\n Options,\n Handle extends NegotiumAdapterHandle<Name>,\n>(options: NegotiumAdapterContractOptions<Name, Options, Handle>): Promise<void> {\n assertNegotiumAdapterDefinition(options.definition, options.name);\n for (const capability of [\"localUserInput\", \"topicManagement\", \"externalPlacedTurn\"] as const) {\n assertNegotiumAdapterCapability(\n options.definition,\n capability,\n options.capabilities[capability],\n );\n }\n if (!options.createHandle) return;\n const handle = await options.createHandle();\n assertNegotiumAdapterHandle(handle, options.name);\n await assertAdapterStopIsIdempotent(handle);\n}\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": ";;AAEO,IAAM,+BAA+B;;;ACKrC,SAAS,+BAAoD,CAClE,OACA,cACwF;AAAA,EACxF,KAAK,SAAS,OAAO,UAAU,UAAU;AAAA,IACvC,MAAM,IAAI,UAAU,WAAW,0CAA0C;AAAA,EAC3E;AAAA,EACA,MAAM,YAAY;AAAA,EAOlB,IAAI,UAAU,eAAe,8BAA8B;AAAA,IACzD,MAAM,IAAI,UAAU,WAAW,8CAA8C;AAAA,EAC/E;AAAA,EACA,IAAI,UAAU,SAAS,gBAAgB,OAAO,UAAU,UAAU,YAAY;AAAA,IAC5E,MAAM,IAAI,UAAU,uCAAuC,0BAA0B;AAAA,EACvF;AAAA,EACA,MAAM,aAAa,UAAU;AAAA,EAC7B,KACG,cACA,WAAW,eAAe,UAAU,WAAW,eAAe,eAC/D,OAAO,WAAW,oBAAoB,aACrC,WAAW,oBAAoB,YAAY,WAAW,oBAAoB,WAC3E;AAAA,IACA,MAAM,IAAI,UAAU,WAAW,8DAA8D;AAAA,EAC/F;AAAA,EACA,MAAM,eAAe,UAAU;AAAA,EAC/B,KACG,gBACD,OAAO,aAAa,mBAAmB,aACvC,OAAO,aAAa,oBAAoB,aACxC,OAAO,aAAa,uBAAuB,WAC3C;AAAA,IACA,MAAM,IAAI,UAAU,WAAW,mDAAmD;AAAA,EACpF;AAAA;AAKK,SAAS,+BAA+B,CAC7C,YACA,YACA,UACM;AAAA,EACN,IAAI,WAAW,aAAa,gBAAgB,UAAU;AAAA,IACpD,MAAM,IAAI,UACR,WAAW,WAAW,mBAAmB,sBAAsB,OAAO,QAAQ,GAChF;AAAA,EACF;AAAA;AAGK,SAAS,2BAAgD,CAC9D,OACA,cAC8C;AAAA,EAC9C,KAAK,SAAS,OAAO,UAAU,UAAU;AAAA,IACvC,MAAM,IAAI,UAAU,WAAW,sCAAsC;AAAA,EACvE;AAAA,EACA,MAAM,YAAY;AAAA,EAClB,IAAI,UAAU,SAAS,cAAc;AAAA,IACnC,MAAM,IAAI,UAAU,+BAA+B,cAAc;AAAA,EACnE;AAAA,EACA,IAAI,OAAO,UAAU,SAAS,YAAY;AAAA,IACxC,MAAM,IAAI,UAAU,WAAW,wCAAwC;AAAA,EACzE;AAAA;AAGF,eAAsB,6BAA6B,CAAC,QAA8C;AAAA,EAChG,MAAM,OAAO,KAAK;AAAA,EAClB,MAAM,OAAO,KAAK;AAAA;AAcpB,eAAsB,6BAIrB,CAAC,SAA+E;AAAA,EAC/E,gCAAgC,QAAQ,YAAY,QAAQ,IAAI;AAAA,EAChE,WAAW,cAAc,CAAC,kBAAkB,mBAAmB,oBAAoB,GAAY;AAAA,IAC7F,gCACE,QAAQ,YACR,YACA,QAAQ,aAAa,WACvB;AAAA,EACF;AAAA,EACA,KAAK,QAAQ;AAAA,IAAc;AAAA,EAC3B,MAAM,SAAS,MAAM,QAAQ,aAAa;AAAA,EAC1C,4BAA4B,QAAQ,QAAQ,IAAI;AAAA,EAChD,MAAM,8BAA8B,MAAM;AAAA;",
|
|
9
|
+
"debugId": "AF9B17C5EFFFA4EA64756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@negotium/adapter-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared lifecycle contract for Negotium channel adapters",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./outbox": {
|
|
15
|
+
"types": "./dist/outbox.d.ts",
|
|
16
|
+
"default": "./dist/outbox.js"
|
|
17
|
+
},
|
|
18
|
+
"./testkit": {
|
|
19
|
+
"types": "./dist/testkit.d.ts",
|
|
20
|
+
"default": "./dist/testkit.js"
|
|
13
21
|
}
|
|
14
22
|
},
|
|
15
23
|
"files": [
|
|
@@ -33,7 +41,7 @@
|
|
|
33
41
|
"access": "public"
|
|
34
42
|
},
|
|
35
43
|
"scripts": {
|
|
36
|
-
"build": "bun ../../scripts/build-package.ts src/index.ts",
|
|
44
|
+
"build": "bun ../../scripts/build-package.ts src/index.ts src/outbox.ts src/testkit.ts",
|
|
37
45
|
"check": "bunx tsc --noEmit",
|
|
38
46
|
"lint": "biome check",
|
|
39
47
|
"test": "bun test",
|