@peers-app/peers-sdk 0.16.5 → 0.17.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.
Files changed (49) hide show
  1. package/dist/contracts/__tests__/builder.test.d.ts +1 -0
  2. package/dist/contracts/__tests__/builder.test.js +426 -0
  3. package/dist/contracts/__tests__/extract.test.d.ts +1 -0
  4. package/dist/contracts/__tests__/extract.test.js +145 -0
  5. package/dist/contracts/__tests__/integration.test.d.ts +1 -0
  6. package/dist/contracts/__tests__/integration.test.js +348 -0
  7. package/dist/contracts/__tests__/registry.test.d.ts +1 -0
  8. package/dist/contracts/__tests__/registry.test.js +324 -0
  9. package/dist/contracts/__tests__/validate.test.d.ts +1 -0
  10. package/dist/contracts/__tests__/validate.test.js +699 -0
  11. package/dist/contracts/builder.d.ts +102 -0
  12. package/dist/contracts/builder.js +216 -0
  13. package/dist/contracts/contract-providers.table.d.ts +40 -0
  14. package/dist/contracts/contract-providers.table.js +41 -0
  15. package/dist/contracts/contracts.table.d.ts +44 -0
  16. package/dist/contracts/contracts.table.js +44 -0
  17. package/dist/contracts/extract.d.ts +46 -0
  18. package/dist/contracts/extract.js +51 -0
  19. package/dist/contracts/index.d.ts +9 -0
  20. package/dist/contracts/index.js +31 -0
  21. package/dist/contracts/persistent-registry.d.ts +32 -0
  22. package/dist/contracts/persistent-registry.js +138 -0
  23. package/dist/contracts/registry.d.ts +58 -0
  24. package/dist/contracts/registry.js +155 -0
  25. package/dist/contracts/types.d.ts +108 -0
  26. package/dist/contracts/types.js +10 -0
  27. package/dist/contracts/validate.d.ts +24 -0
  28. package/dist/contracts/validate.js +274 -0
  29. package/dist/data/assistants.d.ts +5 -0
  30. package/dist/data/assistants.js +1 -0
  31. package/dist/data/package-versions.d.ts +2 -2
  32. package/dist/data/persistent-vars.d.ts +3 -0
  33. package/dist/data/persistent-vars.js +3 -0
  34. package/dist/data/tools.d.ts +5 -2
  35. package/dist/data/tools.js +1 -1
  36. package/dist/data/workflows.d.ts +1 -0
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.js +1 -0
  39. package/dist/package-loader/contract-package-loader.d.ts +23 -0
  40. package/dist/package-loader/contract-package-loader.js +65 -0
  41. package/dist/package-loader/index.d.ts +1 -0
  42. package/dist/package-loader/index.js +1 -0
  43. package/dist/package-loader/package-loader.d.ts +11 -0
  44. package/dist/package-loader/package-loader.js +59 -8
  45. package/dist/rpc-types.d.ts +7 -0
  46. package/dist/rpc-types.js +4 -0
  47. package/dist/types/workflow.d.ts +3 -0
  48. package/dist/types/workflow.js +1 -0
  49. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,426 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const zod_1 = require("zod");
4
+ const types_1 = require("../../data/orm/types");
5
+ const field_type_1 = require("../../types/field-type");
6
+ const utils_1 = require("../../utils");
7
+ const builder_1 = require("../builder");
8
+ const extract_1 = require("../extract");
9
+ const types_2 = require("../types");
10
+ beforeAll(() => {
11
+ (0, extract_1.setSchemaToFieldsFn)(types_1.schemaToFields);
12
+ });
13
+ const TEST_PKG_ID = (0, utils_1.newid)();
14
+ const TASKS_ID = (0, utils_1.newid)();
15
+ const NOTES_ID = (0, utils_1.newid)();
16
+ const AUTH_ID = (0, utils_1.newid)();
17
+ const PROJECTS_ID = (0, utils_1.newid)();
18
+ const FANCY_TASKS_ID = (0, utils_1.newid)();
19
+ const GENERIC_ID = (0, utils_1.newid)();
20
+ describe("definePackage", () => {
21
+ it("produces a contract definition from a single contract with tables and tools", () => {
22
+ const result = (0, builder_1.definePackage)((pkg) => {
23
+ pkg.packageId = TEST_PKG_ID;
24
+ const tasks = pkg.contract(TASKS_ID, 1, "Tasks");
25
+ tasks.tables = [
26
+ {
27
+ metaData: {
28
+ name: "Tasks",
29
+ description: "Task tracking",
30
+ primaryKeyName: "taskId",
31
+ fields: [],
32
+ },
33
+ schema: zod_1.z.object({
34
+ taskId: zod_1.z.string().describe("PK"),
35
+ title: zod_1.z.string().describe("Title"),
36
+ }),
37
+ },
38
+ ];
39
+ tasks.tools = [
40
+ {
41
+ tool: {
42
+ name: "create-task",
43
+ usageDescription: "Creates a task",
44
+ inputSchema: {
45
+ fields: [{ name: "title", type: field_type_1.FieldType.string, description: "Title" }],
46
+ },
47
+ outputSchema: {
48
+ fields: [{ name: "taskId", type: field_type_1.FieldType.id, description: "ID" }],
49
+ },
50
+ },
51
+ },
52
+ ];
53
+ });
54
+ expect(result.contracts).toHaveLength(1);
55
+ const contract = result.contracts[0];
56
+ expect(contract.contractId).toBe(TASKS_ID);
57
+ expect(contract.version).toBe(1);
58
+ expect(contract.name).toBe("Tasks");
59
+ expect(contract.description).toBe("");
60
+ expect(contract.devTag).toBeUndefined();
61
+ expect(contract.tables).toHaveLength(1);
62
+ expect(contract.tables[0].name).toBe("Tasks");
63
+ expect(contract.tables[0].fields).toHaveLength(2);
64
+ expect(contract.tools).toHaveLength(1);
65
+ expect(contract.tools[0].name).toBe("create-task");
66
+ expect(contract.tools[0].inputFields).toHaveLength(1);
67
+ expect(contract.tools[0].outputFields).toHaveLength(1);
68
+ });
69
+ it("supports multiple contracts from one package", () => {
70
+ const result = (0, builder_1.definePackage)((pkg) => {
71
+ pkg.packageId = TEST_PKG_ID;
72
+ const tasks = pkg.contract(TASKS_ID, 1, "Tasks");
73
+ tasks.tables = [
74
+ {
75
+ metaData: {
76
+ name: "Tasks",
77
+ description: "d",
78
+ primaryKeyName: "taskId",
79
+ fields: [{ name: "taskId", type: field_type_1.FieldType.id }],
80
+ },
81
+ },
82
+ ];
83
+ const notes = pkg.contract(NOTES_ID, 1, "Notes");
84
+ notes.tables = [
85
+ {
86
+ metaData: {
87
+ name: "Notes",
88
+ description: "d",
89
+ primaryKeyName: "noteId",
90
+ fields: [{ name: "noteId", type: field_type_1.FieldType.id }],
91
+ },
92
+ },
93
+ ];
94
+ });
95
+ expect(result.contracts).toHaveLength(2);
96
+ expect(result.contracts[0].contractId).toBe(TASKS_ID);
97
+ expect(result.contracts[1].contractId).toBe(NOTES_ID);
98
+ });
99
+ it("records consumed contracts", () => {
100
+ const result = (0, builder_1.definePackage)((pkg) => {
101
+ pkg.packageId = TEST_PKG_ID;
102
+ pkg.consumes(TASKS_ID, 1);
103
+ pkg.consumes(AUTH_ID, 2);
104
+ const projects = pkg.contract(PROJECTS_ID, 1, "Projects");
105
+ projects.tables = [
106
+ {
107
+ metaData: {
108
+ name: "Projects",
109
+ description: "d",
110
+ primaryKeyName: "projectId",
111
+ fields: [{ name: "projectId", type: field_type_1.FieldType.id }],
112
+ },
113
+ },
114
+ ];
115
+ });
116
+ expect(result.consumes).toHaveLength(2);
117
+ expect(result.consumes[0]).toEqual({ contractId: TASKS_ID, version: 1 });
118
+ expect(result.consumes[1]).toEqual({ contractId: AUTH_ID, version: 2 });
119
+ });
120
+ it("records alsoImplements declarations", () => {
121
+ const result = (0, builder_1.definePackage)((pkg) => {
122
+ pkg.packageId = TEST_PKG_ID;
123
+ const fancy = pkg.contract(FANCY_TASKS_ID, 1, "Fancy Tasks");
124
+ fancy.alsoImplements(TASKS_ID, 1);
125
+ fancy.alsoImplements(TASKS_ID, { from: 1, to: 3 });
126
+ fancy.tables = [
127
+ {
128
+ metaData: {
129
+ name: "Tasks",
130
+ description: "d",
131
+ primaryKeyName: "taskId",
132
+ fields: [{ name: "taskId", type: field_type_1.FieldType.id }],
133
+ },
134
+ },
135
+ ];
136
+ });
137
+ const key = (0, types_2.contractKey)(FANCY_TASKS_ID, 1);
138
+ expect(result.alsoImplements.has(key)).toBe(true);
139
+ const decls = result.alsoImplements.get(key) ?? [];
140
+ expect(decls).toHaveLength(2);
141
+ expect(decls[0]).toEqual({ contractId: TASKS_ID, version: 1 });
142
+ expect(decls[1]).toEqual({ contractId: TASKS_ID, version: { from: 1, to: 3 } });
143
+ });
144
+ it("includes package-level assistants and appNavs", () => {
145
+ const assistant = { id: "asst1", name: "Task Bot" };
146
+ const nav = { name: "Tasks", iconClassName: "bi bi-list-task", navigationPath: "app" };
147
+ const result = (0, builder_1.definePackage)((pkg) => {
148
+ pkg.packageId = TEST_PKG_ID;
149
+ pkg.assistants = [assistant];
150
+ pkg.appNavs = [nav];
151
+ const c = pkg.contract(GENERIC_ID, 1, "Generic");
152
+ c.tables = [
153
+ {
154
+ metaData: {
155
+ name: "T",
156
+ description: "d",
157
+ primaryKeyName: "id",
158
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
159
+ },
160
+ },
161
+ ];
162
+ });
163
+ expect(result.assistants).toEqual([assistant]);
164
+ expect(result.appNavs).toEqual([nav]);
165
+ });
166
+ it("supports devTag on contracts", () => {
167
+ const devContractId = (0, utils_1.newid)();
168
+ const result = (0, builder_1.definePackage)((pkg) => {
169
+ pkg.packageId = TEST_PKG_ID;
170
+ const c = pkg.contract(devContractId, 1, "Dev Contract", "dev");
171
+ c.tables = [
172
+ {
173
+ metaData: {
174
+ name: "T",
175
+ description: "d",
176
+ primaryKeyName: "id",
177
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
178
+ },
179
+ },
180
+ ];
181
+ });
182
+ expect(result.contracts[0].devTag).toBe("dev");
183
+ });
184
+ it("supports observables on contracts", () => {
185
+ const obsContractId = (0, utils_1.newid)();
186
+ const result = (0, builder_1.definePackage)((pkg) => {
187
+ pkg.packageId = TEST_PKG_ID;
188
+ const c = pkg.contract(obsContractId, 1, "Observable Contract");
189
+ c.tables = [
190
+ {
191
+ metaData: {
192
+ name: "T",
193
+ description: "d",
194
+ primaryKeyName: "id",
195
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
196
+ },
197
+ },
198
+ ];
199
+ c.observables = [
200
+ {
201
+ name: "taskCount",
202
+ description: "Count of tasks",
203
+ valueType: field_type_1.FieldType.number,
204
+ writable: false,
205
+ },
206
+ ];
207
+ });
208
+ expect(result.contracts[0].observables).toHaveLength(1);
209
+ expect(result.contracts[0].observables[0].name).toBe("taskCount");
210
+ });
211
+ it("uses provided name and description on the contract definition", () => {
212
+ const cid = (0, utils_1.newid)();
213
+ const result = (0, builder_1.definePackage)((pkg) => {
214
+ pkg.packageId = TEST_PKG_ID;
215
+ const c = pkg.contract(cid, 1, "Human-Readable Name");
216
+ c.tables = [
217
+ {
218
+ metaData: {
219
+ name: "T",
220
+ description: "d",
221
+ primaryKeyName: "id",
222
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
223
+ },
224
+ },
225
+ ];
226
+ });
227
+ expect(result.contracts[0].name).toBe("Human-Readable Name");
228
+ expect(result.contracts[0].description).toBe("");
229
+ });
230
+ it("includes packageId, version, and versionTag in the result", () => {
231
+ const cid = (0, utils_1.newid)();
232
+ const result = (0, builder_1.definePackage)((pkg) => {
233
+ pkg.packageId = TEST_PKG_ID;
234
+ pkg.version = "1.2.3";
235
+ pkg.versionTag = "dev";
236
+ const c = pkg.contract(cid, 1, "C");
237
+ c.tables = [
238
+ {
239
+ metaData: {
240
+ name: "T",
241
+ description: "d",
242
+ primaryKeyName: "id",
243
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
244
+ },
245
+ },
246
+ ];
247
+ });
248
+ expect(result.packageId).toBe(TEST_PKG_ID);
249
+ expect(result.version).toBe("1.2.3");
250
+ expect(result.versionTag).toBe("dev");
251
+ });
252
+ it("leaves version and versionTag undefined when not set", () => {
253
+ const cid = (0, utils_1.newid)();
254
+ const result = (0, builder_1.definePackage)((pkg) => {
255
+ pkg.packageId = TEST_PKG_ID;
256
+ const c = pkg.contract(cid, 1, "C");
257
+ c.tables = [
258
+ {
259
+ metaData: {
260
+ name: "T",
261
+ description: "d",
262
+ primaryKeyName: "id",
263
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
264
+ },
265
+ },
266
+ ];
267
+ });
268
+ expect(result.packageId).toBe(TEST_PKG_ID);
269
+ expect(result.version).toBeUndefined();
270
+ expect(result.versionTag).toBeUndefined();
271
+ });
272
+ });
273
+ describe("definePackage — error cases", () => {
274
+ it("throws when packageId is not set", () => {
275
+ const cid = (0, utils_1.newid)();
276
+ expect(() => {
277
+ (0, builder_1.definePackage)((pkg) => {
278
+ pkg.contract(cid, 1, "C");
279
+ });
280
+ }).toThrow("must set packageId");
281
+ });
282
+ it("throws when packageId is not a valid peer ID", () => {
283
+ expect(() => {
284
+ (0, builder_1.definePackage)((pkg) => {
285
+ pkg.packageId = "not-valid";
286
+ });
287
+ }).toThrow("valid peer ID");
288
+ });
289
+ it("allows package with no contracts (UI-only)", () => {
290
+ const nav = { name: "Game", iconClassName: "bi-controller", navigationPath: "play" };
291
+ const result = (0, builder_1.definePackage)((pkg) => {
292
+ pkg.packageId = TEST_PKG_ID;
293
+ pkg.appNavs = [nav];
294
+ });
295
+ expect(result.contracts).toHaveLength(0);
296
+ expect(result.consumes).toHaveLength(0);
297
+ expect(result.toolInstances).toHaveLength(0);
298
+ expect(result.tableDefinitions).toHaveLength(0);
299
+ expect(result.appNavs).toEqual([nav]);
300
+ });
301
+ it("allows consumes-only package with no defined contracts", () => {
302
+ const result = (0, builder_1.definePackage)((pkg) => {
303
+ pkg.packageId = TEST_PKG_ID;
304
+ pkg.consumes(TASKS_ID, 1);
305
+ });
306
+ expect(result.contracts).toHaveLength(0);
307
+ expect(result.consumes).toHaveLength(1);
308
+ expect(result.toolInstances).toHaveLength(0);
309
+ expect(result.tableDefinitions).toHaveLength(0);
310
+ });
311
+ it("throws on duplicate contract ID + version", () => {
312
+ const dupId = (0, utils_1.newid)();
313
+ expect(() => {
314
+ (0, builder_1.definePackage)((pkg) => {
315
+ pkg.packageId = TEST_PKG_ID;
316
+ pkg.contract(dupId, 1, "First");
317
+ pkg.contract(dupId, 1, "Second");
318
+ });
319
+ }).toThrow("Duplicate contract");
320
+ });
321
+ it("allows same contractId with different versions", () => {
322
+ const cid = (0, utils_1.newid)();
323
+ expect(() => {
324
+ (0, builder_1.definePackage)((pkg) => {
325
+ pkg.packageId = TEST_PKG_ID;
326
+ const v1 = pkg.contract(cid, 1, "V1");
327
+ v1.tables = [
328
+ {
329
+ metaData: {
330
+ name: "T",
331
+ description: "d",
332
+ primaryKeyName: "id",
333
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
334
+ },
335
+ },
336
+ ];
337
+ const v2 = pkg.contract(cid, 2, "V2");
338
+ v2.tables = [
339
+ {
340
+ metaData: {
341
+ name: "T",
342
+ description: "d",
343
+ primaryKeyName: "id",
344
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
345
+ },
346
+ },
347
+ ];
348
+ });
349
+ }).not.toThrow();
350
+ });
351
+ it("throws on non-integer version", () => {
352
+ const cid = (0, utils_1.newid)();
353
+ expect(() => {
354
+ (0, builder_1.definePackage)((pkg) => {
355
+ pkg.packageId = TEST_PKG_ID;
356
+ pkg.contract(cid, 1.5, "Bad Version");
357
+ });
358
+ }).toThrow("positive integer");
359
+ });
360
+ it("throws on zero version", () => {
361
+ const cid = (0, utils_1.newid)();
362
+ expect(() => {
363
+ (0, builder_1.definePackage)((pkg) => {
364
+ pkg.packageId = TEST_PKG_ID;
365
+ pkg.contract(cid, 0, "Zero");
366
+ });
367
+ }).toThrow("positive integer");
368
+ });
369
+ it("throws on negative version", () => {
370
+ const cid = (0, utils_1.newid)();
371
+ expect(() => {
372
+ (0, builder_1.definePackage)((pkg) => {
373
+ pkg.packageId = TEST_PKG_ID;
374
+ pkg.contract(cid, -1, "Negative");
375
+ });
376
+ }).toThrow("positive integer");
377
+ });
378
+ it("throws when contractId is not a valid peer ID", () => {
379
+ expect(() => {
380
+ (0, builder_1.definePackage)((pkg) => {
381
+ pkg.packageId = TEST_PKG_ID;
382
+ pkg.contract("tasks001", 1, "Bad ID");
383
+ });
384
+ }).toThrow("valid peer ID");
385
+ });
386
+ it("throws when consumes contractId is not a valid peer ID", () => {
387
+ const cid = (0, utils_1.newid)();
388
+ expect(() => {
389
+ (0, builder_1.definePackage)((pkg) => {
390
+ pkg.packageId = TEST_PKG_ID;
391
+ pkg.consumes("not-a-peer-id", 1);
392
+ const c = pkg.contract(cid, 1, "C");
393
+ c.tables = [
394
+ {
395
+ metaData: {
396
+ name: "T",
397
+ description: "d",
398
+ primaryKeyName: "id",
399
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
400
+ },
401
+ },
402
+ ];
403
+ });
404
+ }).toThrow("valid peer ID");
405
+ });
406
+ it("throws when alsoImplements targetContractId is not a valid peer ID", () => {
407
+ const cid = (0, utils_1.newid)();
408
+ expect(() => {
409
+ (0, builder_1.definePackage)((pkg) => {
410
+ pkg.packageId = TEST_PKG_ID;
411
+ const c = pkg.contract(cid, 1, "C");
412
+ c.alsoImplements("invalid-id", 1);
413
+ c.tables = [
414
+ {
415
+ metaData: {
416
+ name: "T",
417
+ description: "d",
418
+ primaryKeyName: "id",
419
+ fields: [{ name: "id", type: field_type_1.FieldType.id }],
420
+ },
421
+ },
422
+ ];
423
+ });
424
+ }).toThrow("valid peer ID");
425
+ });
426
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const zod_1 = require("zod");
4
+ const types_1 = require("../../data/orm/types");
5
+ const field_type_1 = require("../../types/field-type");
6
+ const extract_1 = require("../extract");
7
+ // Wire in the real schemaToFields before tests run so we don't trigger the
8
+ // lazy require (which would pull in the full DI chain in some environments).
9
+ beforeAll(() => {
10
+ (0, extract_1.setSchemaToFieldsFn)(types_1.schemaToFields);
11
+ });
12
+ describe("extractTableShape", () => {
13
+ it("extracts shape from a Zod schema via schemaToFields", () => {
14
+ const schema = zod_1.z.object({
15
+ taskId: zod_1.z.string().describe("Primary key"),
16
+ title: zod_1.z.string().describe("Task title"),
17
+ done: zod_1.z.boolean().optional().describe("Completion flag"),
18
+ });
19
+ const def = {
20
+ metaData: {
21
+ name: "Tasks",
22
+ description: "Task tracking table",
23
+ primaryKeyName: "taskId",
24
+ fields: [],
25
+ },
26
+ schema,
27
+ };
28
+ const shape = (0, extract_1.extractTableShape)(def);
29
+ expect(shape.name).toBe("Tasks");
30
+ expect(shape.description).toBe("Task tracking table");
31
+ expect(shape.primaryKeyName).toBe("taskId");
32
+ expect(shape.fields).toHaveLength(3);
33
+ const taskIdField = shape.fields.find((f) => f.name === "taskId");
34
+ expect(taskIdField?.type).toBe(field_type_1.FieldType.string);
35
+ const titleField = shape.fields.find((f) => f.name === "title");
36
+ expect(titleField?.type).toBe(field_type_1.FieldType.string);
37
+ const doneField = shape.fields.find((f) => f.name === "done");
38
+ expect(doneField?.type).toBe(field_type_1.FieldType.boolean);
39
+ expect(doneField?.optional).toBe(true);
40
+ });
41
+ it("falls back to metaData.fields when no schema is provided", () => {
42
+ const def = {
43
+ metaData: {
44
+ name: "Legacy",
45
+ description: "A legacy table",
46
+ primaryKeyName: "legacyId",
47
+ fields: [
48
+ { name: "legacyId", type: field_type_1.FieldType.id, description: "Primary key" },
49
+ { name: "label", type: field_type_1.FieldType.string, description: "Label" },
50
+ ],
51
+ },
52
+ };
53
+ const shape = (0, extract_1.extractTableShape)(def);
54
+ expect(shape.name).toBe("Legacy");
55
+ expect(shape.fields).toHaveLength(2);
56
+ expect(shape.fields[0].name).toBe("legacyId");
57
+ expect(shape.fields[1].name).toBe("label");
58
+ });
59
+ it("does not mutate the original metaData.fields array", () => {
60
+ const originalFields = [{ name: "id", type: field_type_1.FieldType.id, description: "PK" }];
61
+ const def = {
62
+ metaData: {
63
+ name: "Test",
64
+ description: "Test",
65
+ primaryKeyName: "id",
66
+ fields: originalFields,
67
+ },
68
+ };
69
+ const shape = (0, extract_1.extractTableShape)(def);
70
+ shape.fields.push({ name: "extra", type: field_type_1.FieldType.string });
71
+ expect(originalFields).toHaveLength(1);
72
+ });
73
+ it("handles arrays and complex Zod types", () => {
74
+ const schema = zod_1.z.object({
75
+ recordId: zod_1.z.string().describe("PK"),
76
+ tags: zod_1.z.string().array().describe("Tag list"),
77
+ metadata: zod_1.z.object({}).optional().describe("Arbitrary metadata"),
78
+ });
79
+ const def = {
80
+ metaData: {
81
+ name: "Records",
82
+ description: "Records with arrays",
83
+ primaryKeyName: "recordId",
84
+ fields: [],
85
+ },
86
+ schema,
87
+ };
88
+ const shape = (0, extract_1.extractTableShape)(def);
89
+ const tagsField = shape.fields.find((f) => f.name === "tags");
90
+ expect(tagsField?.type).toBe(field_type_1.FieldType.string);
91
+ expect(tagsField?.isArray).toBe(true);
92
+ const metadataField = shape.fields.find((f) => f.name === "metadata");
93
+ expect(metadataField?.type).toBe(field_type_1.FieldType.object);
94
+ expect(metadataField?.optional).toBe(true);
95
+ });
96
+ });
97
+ describe("extractToolShape", () => {
98
+ it("extracts name and usageDescription from the tool", () => {
99
+ const inst = {
100
+ tool: {
101
+ name: "create-task",
102
+ usageDescription: "Creates a new task",
103
+ inputSchema: { fields: [] },
104
+ outputSchema: { fields: [] },
105
+ },
106
+ };
107
+ const shape = (0, extract_1.extractToolShape)(inst);
108
+ expect(shape.name).toBe("create-task");
109
+ expect(shape.usageDescription).toBe("Creates a new task");
110
+ });
111
+ it("extracts input and output fields from IOSchema", () => {
112
+ const inst = {
113
+ tool: {
114
+ name: "create-task",
115
+ usageDescription: "Creates a task",
116
+ inputSchema: {
117
+ fields: [
118
+ { name: "title", type: field_type_1.FieldType.string, description: "Task title" },
119
+ { name: "priority", type: field_type_1.FieldType.number, description: "Priority level" },
120
+ ],
121
+ },
122
+ outputSchema: {
123
+ fields: [{ name: "taskId", type: field_type_1.FieldType.id, description: "Created task ID" }],
124
+ },
125
+ },
126
+ };
127
+ const shape = (0, extract_1.extractToolShape)(inst);
128
+ expect(shape.inputFields).toHaveLength(2);
129
+ expect(shape.inputFields[0].name).toBe("title");
130
+ expect(shape.inputFields[1].name).toBe("priority");
131
+ expect(shape.outputFields).toHaveLength(1);
132
+ expect(shape.outputFields[0].name).toBe("taskId");
133
+ });
134
+ it("returns empty field arrays when schemas are missing", () => {
135
+ const inst = {
136
+ tool: {
137
+ name: "no-io-tool",
138
+ usageDescription: "A tool with no I/O",
139
+ },
140
+ };
141
+ const shape = (0, extract_1.extractToolShape)(inst);
142
+ expect(shape.inputFields).toEqual([]);
143
+ expect(shape.outputFields).toEqual([]);
144
+ });
145
+ });
@@ -0,0 +1 @@
1
+ export {};