@frockbot/connection-core 0.1.4 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frockbot/connection-core",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
package/src/index.test.ts CHANGED
@@ -1,14 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import {
3
- ConnectionDependencyRouter,
4
3
  decodeConnectionCommandIdV1,
5
4
  decodeConnectionCommandV1,
6
- decodeConnectionDependencyCommandV1,
7
- decodeConnectionDependencyResultV1,
8
5
  decodeConnectionModelCatalogV1,
9
6
  decodeRevokeConnectionResultV1,
10
7
  decodeStartConnectionResultV1,
11
- type ConnectionDependencyCommandV1,
12
8
  } from "./index.js";
13
9
 
14
10
  describe("Connection result contracts", () => {
@@ -83,77 +79,6 @@ describe("Connection result contracts", () => {
83
79
  }
84
80
  });
85
81
 
86
- test("strictly decodes provider-neutral dependency commands and results", () => {
87
- const claim = {
88
- schemaVersion: 1,
89
- action: "claim",
90
- operationId: "operation-1",
91
- userId: "user-1",
92
- packageId: "mail",
93
- connectionId: "connection-1",
94
- botId: "bot-1",
95
- generation: "generation-1",
96
- requirement: {
97
- schemaVersion: 1,
98
- packageId: "mail",
99
- packageVersion: "1.0.0",
100
- capabilityId: "send",
101
- connectionTypeIds: ["oauth"],
102
- },
103
- } as const satisfies Extract<
104
- ConnectionDependencyCommandV1,
105
- { action: "claim" }
106
- >;
107
- expect(decodeConnectionDependencyCommandV1(claim)).toEqual(claim);
108
- expect(
109
- decodeConnectionDependencyResultV1({
110
- schemaVersion: 1,
111
- status: "pending",
112
- failure: "provider response is uncertain",
113
- }),
114
- ).toMatchObject({ status: "pending" });
115
- for (const invalid of [
116
- { ...claim, extra: true },
117
- { ...claim, requirement: { ...claim.requirement, extra: true } },
118
- { schemaVersion: 1, status: "claimed", failure: "not allowed" },
119
- ]) {
120
- expect(() =>
121
- "action" in invalid
122
- ? decodeConnectionDependencyCommandV1(invalid)
123
- : decodeConnectionDependencyResultV1(invalid),
124
- ).toThrow();
125
- }
126
- });
127
-
128
- test("routes only to a registered Connection-owning Contribution", async () => {
129
- const router = new ConnectionDependencyRouter();
130
- const command = {
131
- schemaVersion: 1,
132
- action: "read",
133
- operationId: "operation-1",
134
- userId: "user-1",
135
- packageId: "mail",
136
- connectionId: "connection-1",
137
- botId: "bot-1",
138
- generation: "generation-1",
139
- } as const;
140
- await expect(router.execute("missing", command)).resolves.toMatchObject({
141
- status: "unavailable",
142
- });
143
- const calls: unknown[] = [];
144
- router.register({
145
- packageId: "mail",
146
- executeDependency: (input) => {
147
- calls.push(input);
148
- return Promise.resolve({ schemaVersion: 1, status: "released" });
149
- },
150
- });
151
- await expect(router.execute("mail", command)).resolves.toMatchObject({
152
- status: "released",
153
- });
154
- expect(calls).toEqual([command]);
155
- });
156
-
157
82
  test("requires an exact versioned revocation result", () => {
158
83
  expect(
159
84
  decodeRevokeConnectionResultV1({
package/src/index.ts CHANGED
@@ -29,88 +29,6 @@ export interface ConnectionCompletionResult {
29
29
  nativeReturnNonce?: string;
30
30
  }
31
31
 
32
- export interface ConnectionDependencyRequirementV1 {
33
- schemaVersion: 1;
34
- packageId: string;
35
- packageVersion: string;
36
- capabilityId: string;
37
- connectionTypeIds: string[];
38
- }
39
-
40
- export type ConnectionDependencyCommandV1 =
41
- | {
42
- schemaVersion: 1;
43
- action: "claim";
44
- operationId: string;
45
- userId: string;
46
- packageId: string;
47
- connectionId: string;
48
- botId: string;
49
- generation: string;
50
- requirement: ConnectionDependencyRequirementV1;
51
- }
52
- | {
53
- schemaVersion: 1;
54
- action: "read" | "acknowledge" | "release" | "reconcile";
55
- operationId: string;
56
- userId: string;
57
- packageId: string;
58
- connectionId: string;
59
- botId: string;
60
- generation: string;
61
- };
62
-
63
- export type ConnectionDependencyResultV1 =
64
- | { schemaVersion: 1; status: "claimed" | "acknowledged" | "released" }
65
- | {
66
- schemaVersion: 1;
67
- status: "pending" | "unavailable" | "absent" | "rejected";
68
- failure?: string;
69
- };
70
-
71
- export interface ConnectionDependencyOwner {
72
- readonly packageId: string;
73
- executeDependency(
74
- command: ConnectionDependencyCommandV1,
75
- ): Promise<ConnectionDependencyResultV1>;
76
- }
77
-
78
- /** Routes dependency commands only to the Contribution owning the Connection Package. */
79
- export class ConnectionDependencyRouter {
80
- private readonly owners = new Map<string, ConnectionDependencyOwner>();
81
-
82
- register(owner: ConnectionDependencyOwner): () => void {
83
- if (this.owners.has(owner.packageId)) {
84
- throw new Error(
85
- `Connection dependency owner "${owner.packageId}" is already registered`,
86
- );
87
- }
88
- this.owners.set(owner.packageId, owner);
89
- return () => {
90
- if (this.owners.get(owner.packageId) === owner)
91
- this.owners.delete(owner.packageId);
92
- };
93
- }
94
-
95
- async execute(
96
- packageId: string,
97
- input: unknown,
98
- ): Promise<ConnectionDependencyResultV1> {
99
- const command = decodeConnectionDependencyCommandV1(input);
100
- const owner = this.owners.get(packageId);
101
- if (!owner) {
102
- return {
103
- schemaVersion: 1,
104
- status: "unavailable",
105
- failure: `Connection Package "${packageId}" has no backend Contribution`,
106
- };
107
- }
108
- return decodeConnectionDependencyResultV1(
109
- await owner.executeDependency(command),
110
- );
111
- }
112
- }
113
-
114
32
  function record(value: unknown): Record<string, unknown> | undefined {
115
33
  return value && typeof value === "object" && !Array.isArray(value)
116
34
  ? (value as Record<string, unknown>)
@@ -209,137 +127,3 @@ export function decodeRevokeConnectionResultV1(
209
127
  }
210
128
  return { schemaVersion: 1, status: value.status };
211
129
  }
212
-
213
- function dependencyIdentity(value: Record<string, unknown>) {
214
- for (const key of [
215
- "operationId",
216
- "userId",
217
- "packageId",
218
- "connectionId",
219
- "botId",
220
- "generation",
221
- ] as const) {
222
- if (!nonemptyString(value[key], 128)) {
223
- throw new Error(`Connection dependency ${key} is invalid`);
224
- }
225
- }
226
- return {
227
- operationId: value.operationId as string,
228
- userId: value.userId as string,
229
- packageId: value.packageId as string,
230
- connectionId: value.connectionId as string,
231
- botId: value.botId as string,
232
- generation: value.generation as string,
233
- };
234
- }
235
-
236
- function dependencyRequirement(
237
- input: unknown,
238
- ): ConnectionDependencyRequirementV1 {
239
- const value = record(input);
240
- if (
241
- !value ||
242
- !hasExactKeys(value, [
243
- "schemaVersion",
244
- "packageId",
245
- "packageVersion",
246
- "capabilityId",
247
- "connectionTypeIds",
248
- ]) ||
249
- value.schemaVersion !== 1 ||
250
- !nonemptyString(value.packageId, 128) ||
251
- !nonemptyString(value.packageVersion, 100) ||
252
- !nonemptyString(value.capabilityId, 128) ||
253
- !Array.isArray(value.connectionTypeIds) ||
254
- value.connectionTypeIds.length === 0 ||
255
- value.connectionTypeIds.length > 64 ||
256
- !value.connectionTypeIds.every((item) => nonemptyString(item, 128))
257
- ) {
258
- throw new Error("Connection dependency requirement is invalid");
259
- }
260
- return {
261
- schemaVersion: 1,
262
- packageId: value.packageId,
263
- packageVersion: value.packageVersion,
264
- capabilityId: value.capabilityId,
265
- connectionTypeIds: [...value.connectionTypeIds],
266
- };
267
- }
268
-
269
- export function decodeConnectionDependencyCommandV1(
270
- input: unknown,
271
- ): ConnectionDependencyCommandV1 {
272
- const value = record(input);
273
- if (!value || value.schemaVersion !== 1) {
274
- throw new Error("Connection dependency command is invalid");
275
- }
276
- const base = [
277
- "schemaVersion",
278
- "action",
279
- "operationId",
280
- "userId",
281
- "packageId",
282
- "connectionId",
283
- "botId",
284
- "generation",
285
- ];
286
- if (value.action === "claim") {
287
- if (!hasExactKeys(value, [...base, "requirement"])) {
288
- throw new Error("Connection dependency command is invalid");
289
- }
290
- return {
291
- schemaVersion: 1,
292
- action: "claim",
293
- ...dependencyIdentity(value),
294
- requirement: dependencyRequirement(value.requirement),
295
- };
296
- }
297
- if (
298
- value.action !== "read" &&
299
- value.action !== "acknowledge" &&
300
- value.action !== "release" &&
301
- value.action !== "reconcile"
302
- ) {
303
- throw new Error("Connection dependency command is invalid");
304
- }
305
- if (!hasExactKeys(value, base)) {
306
- throw new Error("Connection dependency command is invalid");
307
- }
308
- return {
309
- schemaVersion: 1,
310
- action: value.action,
311
- ...dependencyIdentity(value),
312
- };
313
- }
314
-
315
- export function decodeConnectionDependencyResultV1(
316
- input: unknown,
317
- ): ConnectionDependencyResultV1 {
318
- const value = record(input);
319
- if (
320
- !value ||
321
- value.schemaVersion !== 1 ||
322
- (value.status !== "claimed" &&
323
- value.status !== "acknowledged" &&
324
- value.status !== "released" &&
325
- value.status !== "pending" &&
326
- value.status !== "unavailable" &&
327
- value.status !== "absent" &&
328
- value.status !== "rejected") ||
329
- !hasExactKeys(value, ["schemaVersion", "status"], ["failure"]) ||
330
- (value.failure !== undefined && !nonemptyString(value.failure, 512))
331
- ) {
332
- throw new Error("Connection dependency result is invalid");
333
- }
334
- if (
335
- (value.status === "claimed" ||
336
- value.status === "acknowledged" ||
337
- value.status === "released") &&
338
- value.failure !== undefined
339
- ) {
340
- throw new Error("Connection dependency result is invalid");
341
- }
342
- return value.failure === undefined
343
- ? { schemaVersion: 1, status: value.status }
344
- : { schemaVersion: 1, status: value.status, failure: value.failure };
345
- }