@ekanos/sdk 0.1.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 (141) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/dist/components/base-activation-dialog.d.ts +41 -0
  4. package/dist/components/base-activation-dialog.js +41 -0
  5. package/dist/components/base-activation-dialog.js.map +1 -0
  6. package/dist/components/base-activation-form.d.ts +36 -0
  7. package/dist/components/base-activation-form.js +21 -0
  8. package/dist/components/base-activation-form.js.map +1 -0
  9. package/dist/components/base-marketplace-tile.d.ts +34 -0
  10. package/dist/components/base-marketplace-tile.js +40 -0
  11. package/dist/components/base-marketplace-tile.js.map +1 -0
  12. package/dist/components/current-return-path.d.ts +19 -0
  13. package/dist/components/current-return-path.js +26 -0
  14. package/dist/components/current-return-path.js.map +1 -0
  15. package/dist/components/index.d.ts +40 -0
  16. package/dist/components/index.js +38 -0
  17. package/dist/components/index.js.map +1 -0
  18. package/dist/components/oauth-activation-form.d.ts +50 -0
  19. package/dist/components/oauth-activation-form.js +119 -0
  20. package/dist/components/oauth-activation-form.js.map +1 -0
  21. package/dist/components/widgets/ai-prompt-chip.d.ts +31 -0
  22. package/dist/components/widgets/ai-prompt-chip.js +38 -0
  23. package/dist/components/widgets/ai-prompt-chip.js.map +1 -0
  24. package/dist/components/widgets/ask-icon.d.ts +3 -0
  25. package/dist/components/widgets/ask-icon.js +5 -0
  26. package/dist/components/widgets/ask-icon.js.map +1 -0
  27. package/dist/components/widgets/use-animated-height.d.ts +12 -0
  28. package/dist/components/widgets/use-animated-height.js +39 -0
  29. package/dist/components/widgets/use-animated-height.js.map +1 -0
  30. package/dist/components/widgets/widget-ask-bar.d.ts +18 -0
  31. package/dist/components/widgets/widget-ask-bar.js +62 -0
  32. package/dist/components/widgets/widget-ask-bar.js.map +1 -0
  33. package/dist/components/widgets/widget-context.d.ts +38 -0
  34. package/dist/components/widgets/widget-context.js +4 -0
  35. package/dist/components/widgets/widget-context.js.map +1 -0
  36. package/dist/components/widgets/widget-state-components.d.ts +5 -0
  37. package/dist/components/widgets/widget-state-components.js +19 -0
  38. package/dist/components/widgets/widget-state-components.js.map +1 -0
  39. package/dist/components/widgets/widget.d.ts +91 -0
  40. package/dist/components/widgets/widget.js +205 -0
  41. package/dist/components/widgets/widget.js.map +1 -0
  42. package/dist/context/egress.d.ts +30 -0
  43. package/dist/context/egress.js +81 -0
  44. package/dist/context/egress.js.map +1 -0
  45. package/dist/context/errors.d.ts +57 -0
  46. package/dist/context/errors.js +78 -0
  47. package/dist/context/errors.js.map +1 -0
  48. package/dist/context/index.d.ts +19 -0
  49. package/dist/context/index.js +20 -0
  50. package/dist/context/index.js.map +1 -0
  51. package/dist/context/require-context.d.ts +26 -0
  52. package/dist/context/require-context.js +36 -0
  53. package/dist/context/require-context.js.map +1 -0
  54. package/dist/context/storage-validation.d.ts +71 -0
  55. package/dist/context/storage-validation.js +132 -0
  56. package/dist/context/storage-validation.js.map +1 -0
  57. package/dist/context/types.d.ts +11 -0
  58. package/dist/context/types.js +2 -0
  59. package/dist/context/types.js.map +1 -0
  60. package/dist/hooks/activation-actions-context.d.ts +80 -0
  61. package/dist/hooks/activation-actions-context.js +31 -0
  62. package/dist/hooks/activation-actions-context.js.map +1 -0
  63. package/dist/hooks/ask-assistant-context.d.ts +32 -0
  64. package/dist/hooks/ask-assistant-context.js +56 -0
  65. package/dist/hooks/ask-assistant-context.js.map +1 -0
  66. package/dist/hooks/fetch-integration-storage.d.ts +48 -0
  67. package/dist/hooks/fetch-integration-storage.js +68 -0
  68. package/dist/hooks/fetch-integration-storage.js.map +1 -0
  69. package/dist/hooks/index.d.ts +28 -0
  70. package/dist/hooks/index.js +32 -0
  71. package/dist/hooks/index.js.map +1 -0
  72. package/dist/hooks/use-activate-integration.d.ts +15 -0
  73. package/dist/hooks/use-activate-integration.js +61 -0
  74. package/dist/hooks/use-activate-integration.js.map +1 -0
  75. package/dist/hooks/use-oauth-connection-status.d.ts +360 -0
  76. package/dist/hooks/use-oauth-connection-status.js +108 -0
  77. package/dist/hooks/use-oauth-connection-status.js.map +1 -0
  78. package/dist/hooks/use-widget-events.d.ts +20 -0
  79. package/dist/hooks/use-widget-events.js +51 -0
  80. package/dist/hooks/use-widget-events.js.map +1 -0
  81. package/dist/hooks/widget-event-context.d.ts +22 -0
  82. package/dist/hooks/widget-event-context.js +75 -0
  83. package/dist/hooks/widget-event-context.js.map +1 -0
  84. package/dist/hooks/widget-event-types.d.ts +55 -0
  85. package/dist/hooks/widget-event-types.js +9 -0
  86. package/dist/hooks/widget-event-types.js.map +1 -0
  87. package/dist/index.d.ts +19 -0
  88. package/dist/index.js +19 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/integration/cron.d.ts +29 -0
  91. package/dist/integration/cron.js +102 -0
  92. package/dist/integration/cron.js.map +1 -0
  93. package/dist/integration/define-integration.d.ts +28 -0
  94. package/dist/integration/define-integration.js +41 -0
  95. package/dist/integration/define-integration.js.map +1 -0
  96. package/dist/integration/event-surfaces.d.ts +15 -0
  97. package/dist/integration/event-surfaces.js +45 -0
  98. package/dist/integration/event-surfaces.js.map +1 -0
  99. package/dist/integration/index.d.ts +20 -0
  100. package/dist/integration/index.js +23 -0
  101. package/dist/integration/index.js.map +1 -0
  102. package/dist/integration/types.d.ts +7 -0
  103. package/dist/integration/types.js +2 -0
  104. package/dist/integration/types.js.map +1 -0
  105. package/dist/mcp/guards.d.ts +40 -0
  106. package/dist/mcp/guards.js +99 -0
  107. package/dist/mcp/guards.js.map +1 -0
  108. package/dist/mcp/index.d.ts +22 -0
  109. package/dist/mcp/index.js +22 -0
  110. package/dist/mcp/index.js.map +1 -0
  111. package/dist/mcp/ownership.d.ts +74 -0
  112. package/dist/mcp/ownership.js +83 -0
  113. package/dist/mcp/ownership.js.map +1 -0
  114. package/dist/mcp/types.d.ts +145 -0
  115. package/dist/mcp/types.js +2 -0
  116. package/dist/mcp/types.js.map +1 -0
  117. package/dist/testing/index.d.ts +16 -0
  118. package/dist/testing/index.js +19 -0
  119. package/dist/testing/index.js.map +1 -0
  120. package/dist/testing/invoke.d.ts +77 -0
  121. package/dist/testing/invoke.js +83 -0
  122. package/dist/testing/invoke.js.map +1 -0
  123. package/dist/testing/mock-context.d.ts +79 -0
  124. package/dist/testing/mock-context.js +206 -0
  125. package/dist/testing/mock-context.js.map +1 -0
  126. package/dist/types/icon.d.ts +12 -0
  127. package/dist/types/icon.js +2 -0
  128. package/dist/types/icon.js.map +1 -0
  129. package/dist/types/index.d.ts +10 -0
  130. package/dist/types/index.js +8 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/dist/types/integration.d.ts +258 -0
  133. package/dist/types/integration.js +2 -0
  134. package/dist/types/integration.js.map +1 -0
  135. package/dist/types/widget-ask-context.d.ts +36 -0
  136. package/dist/types/widget-ask-context.js +2 -0
  137. package/dist/types/widget-ask-context.js.map +1 -0
  138. package/dist/types/workspace-target.d.ts +33 -0
  139. package/dist/types/workspace-target.js +2 -0
  140. package/dist/types/workspace-target.js.map +1 -0
  141. package/package.json +116 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @ekanos/sdk/testing — the partner test harness surface.
3
+ *
4
+ * `createMockContext()` is the day-1 in-memory `IntegrationContext`
5
+ * (proposal §4): partners code and test against `ctx` here before anything
6
+ * lands in a sandbox. Self-contained — no Supabase, no Vault, no network —
7
+ * and enforcement (storage schemas, secret tiers, egress) is the same
8
+ * shared implementation from `@ekanos/sdk/context` that production uses.
9
+ *
10
+ * Surface discipline: additions require an entry in
11
+ * docs/devex/sdk-export-map.md ("Context & testing entrypoints (P1)").
12
+ */
13
+ export { createMockContext } from './mock-context.js';
14
+ // Event-surface invocation: run declared webhook/schedule handlers locally,
15
+ // with the same payload-validation and context-derivation semantics the real
16
+ // transports will have. Used by partner unit tests and the harness Triggers
17
+ // panel alike.
18
+ export { invokeWebhook, invokeSchedule } from './invoke.js';
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AASnD,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,eAAe;AACf,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC","sourcesContent":["/**\n * @ekanos/sdk/testing — the partner test harness surface.\n *\n * `createMockContext()` is the day-1 in-memory `IntegrationContext`\n * (proposal §4): partners code and test against `ctx` here before anything\n * lands in a sandbox. Self-contained — no Supabase, no Vault, no network —\n * and enforcement (storage schemas, secret tiers, egress) is the same\n * shared implementation from `@ekanos/sdk/context` that production uses.\n *\n * Surface discipline: additions require an entry in\n * docs/devex/sdk-export-map.md (\"Context & testing entrypoints (P1)\").\n */\n\nexport { createMockContext } from './mock-context';\nexport type {\n MockContextOptions,\n MockFetchHandler,\n MockIntegrationContext,\n RecordedFetchCall,\n RecordedLog,\n} from './mock-context';\n\n// Event-surface invocation: run declared webhook/schedule handlers locally,\n// with the same payload-validation and context-derivation semantics the real\n// transports will have. Used by partner unit tests and the harness Triggers\n// panel alike.\nexport { invokeWebhook, invokeSchedule } from './invoke';\nexport type {\n DefinitionContextOptions,\n InvokeWebhookOptions,\n InvokeScheduleOptions,\n WebhookInvocationOutcome,\n ScheduleInvocationOutcome,\n} from './invoke';\n"]}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Local execution of the declared event surfaces — `invokeWebhook()` and
3
+ * `invokeSchedule()` deliver to a definition's handlers exactly the way a
4
+ * transport would, against a `createMockContext()` built FROM the definition
5
+ * (its slug, storage schemas, and egress list), so a handler unit test
6
+ * exercises the same schemas and the same egress allowlist production will.
7
+ *
8
+ * Transport semantics reproduced here, so they cannot drift from the docs:
9
+ * - the payload is parsed against `payloadSchema` BEFORE the handler runs;
10
+ * an invalid payload throws and the handler never sees it;
11
+ * - signature verification is the TRANSPORT's job — the local transport
12
+ * logs it as skipped (one warn line on `ctx.logs`), it never verifies and
13
+ * the handler never does either;
14
+ * - a schedule invocation carries `{scheduledFor, invokedAt, trigger}` with
15
+ * `trigger: 'manual'` by default (a human pressed the button).
16
+ */
17
+ import type { IntegrationDefinition, ScheduleInvocation, ScheduleResult, StorageSchemas, WebhookEvent, WebhookResult } from '@ekanos/integration-schema';
18
+ import { type MockContextOptions, type MockIntegrationContext } from './mock-context.js';
19
+ /**
20
+ * Everything `createMockContext` takes except the fields the definition
21
+ * itself is the authority on — the helpers derive `integration`,
22
+ * `storageSchemas`, and `egress` from the definition so a test cannot
23
+ * accidentally run a handler against schemas or an allowlist the definition
24
+ * does not declare.
25
+ */
26
+ export type DefinitionContextOptions<Schemas extends StorageSchemas = StorageSchemas> = Omit<MockContextOptions<Schemas>, 'integration' | 'storageSchemas' | 'egress'>;
27
+ interface BaseInvokeOptions<Schemas extends StorageSchemas> {
28
+ /**
29
+ * Reuse an existing mock context so state (storage, secrets, logs)
30
+ * accumulates across invocations — the harness does this. When set,
31
+ * `context` wins and `contextOptions` must be omitted.
32
+ */
33
+ context?: MockIntegrationContext<Schemas>;
34
+ /** Seeds and stubs for the context the helper creates. */
35
+ contextOptions?: DefinitionContextOptions<Schemas>;
36
+ }
37
+ export interface InvokeWebhookOptions<Schemas extends StorageSchemas = StorageSchemas> extends BaseInvokeOptions<Schemas> {
38
+ /** Delivery headers the event carries. Defaults to `{}`. */
39
+ headers?: Record<string, string>;
40
+ /** Transport-assigned event id. Defaults to a random UUID. */
41
+ eventId?: string;
42
+ /** ISO receipt time. Defaults to now. */
43
+ receivedAt?: string;
44
+ }
45
+ export interface InvokeScheduleOptions<Schemas extends StorageSchemas = StorageSchemas> extends BaseInvokeOptions<Schemas> {
46
+ /** Defaults to `'manual'` — a human pressed "Run now". */
47
+ trigger?: ScheduleInvocation['trigger'];
48
+ /** The tick this invocation stands for. Defaults to now. */
49
+ scheduledFor?: string;
50
+ }
51
+ export interface WebhookInvocationOutcome<Schemas extends StorageSchemas = StorageSchemas> {
52
+ result: WebhookResult;
53
+ /** The event the handler received (payload already schema-parsed). */
54
+ event: WebhookEvent;
55
+ /** The context the handler ran against — assert on its recordings. */
56
+ ctx: MockIntegrationContext<Schemas>;
57
+ }
58
+ export interface ScheduleInvocationOutcome<Schemas extends StorageSchemas = StorageSchemas> {
59
+ result: ScheduleResult;
60
+ invocation: ScheduleInvocation;
61
+ ctx: MockIntegrationContext<Schemas>;
62
+ }
63
+ /**
64
+ * Delivers one payload to one declared webhook, the way a transport would.
65
+ * Throws if the id is undeclared or the payload fails `payloadSchema`;
66
+ * returns the handler's result plus the event and the context it ran
67
+ * against. Never verifies signatures — that is the transport's job, and the
68
+ * local transport records the skip as a `warn` log line.
69
+ */
70
+ export declare function invokeWebhook<Schemas extends StorageSchemas = StorageSchemas>(definition: IntegrationDefinition<Schemas>, webhookId: string, payload: unknown, options?: InvokeWebhookOptions<Schemas>): Promise<WebhookInvocationOutcome<Schemas>>;
71
+ /**
72
+ * Fires one declared schedule, the way the scheduler would. Throws if the id
73
+ * is undeclared; returns the handler's result plus the invocation and the
74
+ * context it ran against.
75
+ */
76
+ export declare function invokeSchedule<Schemas extends StorageSchemas = StorageSchemas>(definition: IntegrationDefinition<Schemas>, scheduleId: string, options?: InvokeScheduleOptions<Schemas>): Promise<ScheduleInvocationOutcome<Schemas>>;
77
+ export {};
@@ -0,0 +1,83 @@
1
+ import { createMockContext, } from './mock-context.js';
2
+ function contextFor(definition, options) {
3
+ var _a, _b;
4
+ if (options.context) {
5
+ if (options.contextOptions) {
6
+ throw new TypeError('Pass either `context` (reuse an existing mock context) or ' +
7
+ '`contextOptions` (seed a fresh one), not both — seeds cannot be ' +
8
+ 'applied to a context that already exists.');
9
+ }
10
+ return options.context;
11
+ }
12
+ return createMockContext(Object.assign(Object.assign(Object.assign(Object.assign({}, ((_a = options.contextOptions) !== null && _a !== void 0 ? _a : {})), { integration: { slug: definition.slug } }), (definition.storage ? { storageSchemas: definition.storage } : {})), { egress: (_b = definition.egress) !== null && _b !== void 0 ? _b : [] }));
13
+ }
14
+ function listIds(ids) {
15
+ return ids.length > 0 ? ids.map((id) => `"${id}"`).join(', ') : '(none)';
16
+ }
17
+ /**
18
+ * Delivers one payload to one declared webhook, the way a transport would.
19
+ * Throws if the id is undeclared or the payload fails `payloadSchema`;
20
+ * returns the handler's result plus the event and the context it ran
21
+ * against. Never verifies signatures — that is the transport's job, and the
22
+ * local transport records the skip as a `warn` log line.
23
+ */
24
+ export async function invokeWebhook(definition, webhookId, payload, options = {}) {
25
+ var _a, _b, _c, _d, _e;
26
+ const webhook = ((_a = definition.webhooks) !== null && _a !== void 0 ? _a : []).find((w) => w.id === webhookId);
27
+ if (!webhook) {
28
+ throw new Error(`Integration "${definition.slug}" declares no webhook "${webhookId}". ` +
29
+ `Declared webhook ids: ${listIds(((_b = definition.webhooks) !== null && _b !== void 0 ? _b : []).map((w) => w.id))}.`);
30
+ }
31
+ const parsed = webhook.payloadSchema.safeParse(payload);
32
+ if (!parsed.success) {
33
+ const issues = parsed.error.issues
34
+ .map((issue) => {
35
+ const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';
36
+ return ` - ${path}: ${issue.message}`;
37
+ })
38
+ .join('\n');
39
+ throw new Error(`Payload rejected by webhook "${webhookId}"'s payloadSchema — the ` +
40
+ `transport refuses such a delivery before the handler runs:\n${issues}`);
41
+ }
42
+ const ctx = contextFor(definition, options);
43
+ if (webhook.signature !== 'none') {
44
+ ctx.logger.warn({
45
+ webhookId,
46
+ header: webhook.signature.header,
47
+ secretName: webhook.signature.secretName,
48
+ }, 'Signature verification SKIPPED (local transport). The host ingress ' +
49
+ 'verifies this header against the named secret before the handler ' +
50
+ 'runs — handlers never verify signatures themselves.');
51
+ }
52
+ const event = {
53
+ id: (_c = options.eventId) !== null && _c !== void 0 ? _c : `evt_${crypto.randomUUID()}`,
54
+ receivedAt: (_d = options.receivedAt) !== null && _d !== void 0 ? _d : new Date().toISOString(),
55
+ headers: (_e = options.headers) !== null && _e !== void 0 ? _e : {},
56
+ payload: parsed.data,
57
+ };
58
+ const result = await webhook.handler(ctx, event);
59
+ return { result, event, ctx };
60
+ }
61
+ /**
62
+ * Fires one declared schedule, the way the scheduler would. Throws if the id
63
+ * is undeclared; returns the handler's result plus the invocation and the
64
+ * context it ran against.
65
+ */
66
+ export async function invokeSchedule(definition, scheduleId, options = {}) {
67
+ var _a, _b, _c, _d;
68
+ const schedule = ((_a = definition.schedules) !== null && _a !== void 0 ? _a : []).find((s) => s.id === scheduleId);
69
+ if (!schedule) {
70
+ throw new Error(`Integration "${definition.slug}" declares no schedule "${scheduleId}". ` +
71
+ `Declared schedule ids: ${listIds(((_b = definition.schedules) !== null && _b !== void 0 ? _b : []).map((s) => s.id))}.`);
72
+ }
73
+ const ctx = contextFor(definition, options);
74
+ const now = new Date().toISOString();
75
+ const invocation = {
76
+ scheduledFor: (_c = options.scheduledFor) !== null && _c !== void 0 ? _c : now,
77
+ invokedAt: now,
78
+ trigger: (_d = options.trigger) !== null && _d !== void 0 ? _d : 'manual',
79
+ };
80
+ const result = await schedule.handler(ctx, invocation);
81
+ return { result, invocation, ctx };
82
+ }
83
+ //# sourceMappingURL=invoke.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoke.js","sourceRoot":"","sources":["../../src/testing/invoke.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Local execution of the declared event surfaces — `invokeWebhook()` and\n * `invokeSchedule()` deliver to a definition's handlers exactly the way a\n * transport would, against a `createMockContext()` built FROM the definition\n * (its slug, storage schemas, and egress list), so a handler unit test\n * exercises the same schemas and the same egress allowlist production will.\n *\n * Transport semantics reproduced here, so they cannot drift from the docs:\n * - the payload is parsed against `payloadSchema` BEFORE the handler runs;\n * an invalid payload throws and the handler never sees it;\n * - signature verification is the TRANSPORT's job — the local transport\n * logs it as skipped (one warn line on `ctx.logs`), it never verifies and\n * the handler never does either;\n * - a schedule invocation carries `{scheduledFor, invokedAt, trigger}` with\n * `trigger: 'manual'` by default (a human pressed the button).\n */\nimport type {\n IntegrationDefinition,\n ScheduleInvocation,\n ScheduleResult,\n StorageSchemas,\n WebhookEvent,\n WebhookResult,\n} from '@ekanos/integration-schema';\n\nimport {\n type MockContextOptions,\n type MockIntegrationContext,\n createMockContext,\n} from './mock-context';\n\n/**\n * Everything `createMockContext` takes except the fields the definition\n * itself is the authority on — the helpers derive `integration`,\n * `storageSchemas`, and `egress` from the definition so a test cannot\n * accidentally run a handler against schemas or an allowlist the definition\n * does not declare.\n */\nexport type DefinitionContextOptions<\n Schemas extends StorageSchemas = StorageSchemas,\n> = Omit<\n MockContextOptions<Schemas>,\n 'integration' | 'storageSchemas' | 'egress'\n>;\n\ninterface BaseInvokeOptions<Schemas extends StorageSchemas> {\n /**\n * Reuse an existing mock context so state (storage, secrets, logs)\n * accumulates across invocations — the harness does this. When set,\n * `context` wins and `contextOptions` must be omitted.\n */\n context?: MockIntegrationContext<Schemas>;\n /** Seeds and stubs for the context the helper creates. */\n contextOptions?: DefinitionContextOptions<Schemas>;\n}\n\nexport interface InvokeWebhookOptions<\n Schemas extends StorageSchemas = StorageSchemas,\n> extends BaseInvokeOptions<Schemas> {\n /** Delivery headers the event carries. Defaults to `{}`. */\n headers?: Record<string, string>;\n /** Transport-assigned event id. Defaults to a random UUID. */\n eventId?: string;\n /** ISO receipt time. Defaults to now. */\n receivedAt?: string;\n}\n\nexport interface InvokeScheduleOptions<\n Schemas extends StorageSchemas = StorageSchemas,\n> extends BaseInvokeOptions<Schemas> {\n /** Defaults to `'manual'` — a human pressed \"Run now\". */\n trigger?: ScheduleInvocation['trigger'];\n /** The tick this invocation stands for. Defaults to now. */\n scheduledFor?: string;\n}\n\nexport interface WebhookInvocationOutcome<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n result: WebhookResult;\n /** The event the handler received (payload already schema-parsed). */\n event: WebhookEvent;\n /** The context the handler ran against — assert on its recordings. */\n ctx: MockIntegrationContext<Schemas>;\n}\n\nexport interface ScheduleInvocationOutcome<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n result: ScheduleResult;\n invocation: ScheduleInvocation;\n ctx: MockIntegrationContext<Schemas>;\n}\n\nfunction contextFor<Schemas extends StorageSchemas>(\n definition: IntegrationDefinition<Schemas>,\n options: BaseInvokeOptions<Schemas>,\n): MockIntegrationContext<Schemas> {\n if (options.context) {\n if (options.contextOptions) {\n throw new TypeError(\n 'Pass either `context` (reuse an existing mock context) or ' +\n '`contextOptions` (seed a fresh one), not both — seeds cannot be ' +\n 'applied to a context that already exists.',\n );\n }\n return options.context;\n }\n\n return createMockContext<Schemas>({\n ...(options.contextOptions ?? {}),\n integration: { slug: definition.slug },\n ...(definition.storage ? { storageSchemas: definition.storage } : {}),\n egress: definition.egress ?? [],\n });\n}\n\nfunction listIds(ids: readonly string[]): string {\n return ids.length > 0 ? ids.map((id) => `\"${id}\"`).join(', ') : '(none)';\n}\n\n/**\n * Delivers one payload to one declared webhook, the way a transport would.\n * Throws if the id is undeclared or the payload fails `payloadSchema`;\n * returns the handler's result plus the event and the context it ran\n * against. Never verifies signatures — that is the transport's job, and the\n * local transport records the skip as a `warn` log line.\n */\nexport async function invokeWebhook<\n Schemas extends StorageSchemas = StorageSchemas,\n>(\n definition: IntegrationDefinition<Schemas>,\n webhookId: string,\n payload: unknown,\n options: InvokeWebhookOptions<Schemas> = {},\n): Promise<WebhookInvocationOutcome<Schemas>> {\n const webhook = (definition.webhooks ?? []).find((w) => w.id === webhookId);\n\n if (!webhook) {\n throw new Error(\n `Integration \"${definition.slug}\" declares no webhook \"${webhookId}\". ` +\n `Declared webhook ids: ${listIds((definition.webhooks ?? []).map((w) => w.id))}.`,\n );\n }\n\n const parsed = webhook.payloadSchema.safeParse(payload);\n\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return ` - ${path}: ${issue.message}`;\n })\n .join('\\n');\n throw new Error(\n `Payload rejected by webhook \"${webhookId}\"'s payloadSchema — the ` +\n `transport refuses such a delivery before the handler runs:\\n${issues}`,\n );\n }\n\n const ctx = contextFor(definition, options);\n\n if (webhook.signature !== 'none') {\n ctx.logger.warn(\n {\n webhookId,\n header: webhook.signature.header,\n secretName: webhook.signature.secretName,\n },\n 'Signature verification SKIPPED (local transport). The host ingress ' +\n 'verifies this header against the named secret before the handler ' +\n 'runs — handlers never verify signatures themselves.',\n );\n }\n\n const event: WebhookEvent = {\n id: options.eventId ?? `evt_${crypto.randomUUID()}`,\n receivedAt: options.receivedAt ?? new Date().toISOString(),\n headers: options.headers ?? {},\n payload: parsed.data,\n };\n\n const result = await webhook.handler(ctx, event);\n\n return { result, event, ctx };\n}\n\n/**\n * Fires one declared schedule, the way the scheduler would. Throws if the id\n * is undeclared; returns the handler's result plus the invocation and the\n * context it ran against.\n */\nexport async function invokeSchedule<\n Schemas extends StorageSchemas = StorageSchemas,\n>(\n definition: IntegrationDefinition<Schemas>,\n scheduleId: string,\n options: InvokeScheduleOptions<Schemas> = {},\n): Promise<ScheduleInvocationOutcome<Schemas>> {\n const schedule = (definition.schedules ?? []).find(\n (s) => s.id === scheduleId,\n );\n\n if (!schedule) {\n throw new Error(\n `Integration \"${definition.slug}\" declares no schedule \"${scheduleId}\". ` +\n `Declared schedule ids: ${listIds((definition.schedules ?? []).map((s) => s.id))}.`,\n );\n }\n\n const ctx = contextFor(definition, options);\n const now = new Date().toISOString();\n\n const invocation: ScheduleInvocation = {\n scheduledFor: options.scheduledFor ?? now,\n invokedAt: now,\n trigger: options.trigger ?? 'manual',\n };\n\n const result = await schedule.handler(ctx, invocation);\n\n return { result, invocation, ctx };\n}\n"]}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `createMockContext()` — the day-1 in-memory `IntegrationContext` for the
3
+ * partner harness (proposal §4, amended by rulings 1–4). Self-contained:
4
+ * nothing here touches Supabase, Vault, or the network. Enforcement is the
5
+ * SAME shared implementation the production wrapper uses
6
+ * (`@ekanos/sdk/context`): schema validation via `parseStorageValue`, egress
7
+ * via `isEgressAllowed` — a test that passes the mock cannot be a schema or
8
+ * egress surprise later.
9
+ */
10
+ import type { z } from 'zod';
11
+ import type { IntegrationActor, IntegrationContext, StorageKeySchema, StorageSchemaMap, StorageSchemas } from '../context/types.js';
12
+ type ScopeSeed<M> = M extends StorageSchemaMap ? {
13
+ [K in keyof M & string]?: z.input<StorageKeySchema<M[K]>>;
14
+ } : Record<never, never>;
15
+ export interface MockFetchHandler {
16
+ /** A string matches URLs that start with it; a RegExp is tested against the full URL. */
17
+ match: string | RegExp;
18
+ respond: (request: Request) => Response | Promise<Response>;
19
+ }
20
+ export interface MockContextOptions<Schemas extends StorageSchemas = StorageSchemas> {
21
+ accountId?: string;
22
+ accountSlug?: string | null;
23
+ userId?: string | null;
24
+ actor?: IntegrationActor;
25
+ sourceId?: string | null;
26
+ timezone?: string | null;
27
+ integration: {
28
+ slug: string;
29
+ productId?: string;
30
+ };
31
+ /**
32
+ * The storage declaration `defineIntegration({ storage })` will carry —
33
+ * the mock enforces the identical schemas via the same shared validator
34
+ * (ruling 1). Omit it and every storage access throws.
35
+ */
36
+ storageSchemas?: Schemas;
37
+ /** Seed rows: key → data, per scope. Validated on construction via the write path. */
38
+ storage?: {
39
+ account?: ScopeSeed<Schemas['account']>;
40
+ user?: ScopeSeed<Schemas['user']>;
41
+ };
42
+ /**
43
+ * Seed secrets, by tier (ruling 2): `account` values are the rows this
44
+ * integration stored — writable via `secrets.set()`; `admin` values stand
45
+ * in for admin-issued source/global-tier credentials — readable, and a
46
+ * `set()` against a name resolvable only there throws `SecretAccessError`.
47
+ */
48
+ secrets?: {
49
+ account?: Record<string, string>;
50
+ admin?: Record<string, string>;
51
+ };
52
+ /** Same allowlist the manifest would declare — enforced by the stub. */
53
+ egress?: readonly string[];
54
+ /** Programmable responses; unmatched allowed calls resolve 200 `{}`. */
55
+ fetchHandlers?: MockFetchHandler[];
56
+ }
57
+ export interface RecordedFetchCall {
58
+ url: string;
59
+ init?: RequestInit;
60
+ denied: boolean;
61
+ }
62
+ export interface RecordedLog {
63
+ level: 'debug' | 'info' | 'warn' | 'error';
64
+ context: Record<string, unknown>;
65
+ message: string;
66
+ }
67
+ export interface MockIntegrationContext<Schemas extends StorageSchemas = StorageSchemas> extends IntegrationContext<Schemas> {
68
+ /** Every ctx.fetch call, allowed or denied, in order. */
69
+ readonly fetchCalls: readonly RecordedFetchCall[];
70
+ /** Every log line, per level. */
71
+ readonly logs: readonly RecordedLog[];
72
+ /** Current readable storage state (expired entries excluded), key → data. */
73
+ dumpStorage(): {
74
+ account: Record<string, unknown>;
75
+ user: Record<string, unknown>;
76
+ };
77
+ }
78
+ export declare function createMockContext<Schemas extends StorageSchemas = StorageSchemas>(options: MockContextOptions<Schemas>): MockIntegrationContext<Schemas>;
79
+ export {};
@@ -0,0 +1,206 @@
1
+ import { isEgressAllowed } from '../context/egress.js';
2
+ import { EgressDeniedError, SecretAccessError } from '../context/errors.js';
3
+ import { assertStorageKeyDeclared, assertStorageKeyWritable, parseStorageValue, } from '../context/storage-validation.js';
4
+ function isExpired(row) {
5
+ return row.expiresAt !== null && Date.parse(row.expiresAt) <= Date.now();
6
+ }
7
+ function parseExpiresAt(options) {
8
+ const expiresAt = options === null || options === void 0 ? void 0 : options.expiresAt;
9
+ if (expiresAt === undefined)
10
+ return null;
11
+ if (Number.isNaN(Date.parse(expiresAt))) {
12
+ throw new TypeError(`expiresAt "${expiresAt}" is not a parseable timestamp. Pass an ` +
13
+ `ISO-8601 string, e.g. new Date(Date.now() + 60_000).toISOString().`);
14
+ }
15
+ return expiresAt;
16
+ }
17
+ function createInMemoryStore(args) {
18
+ const { scope, schemas, rows, guard } = args;
19
+ return {
20
+ async get(key) {
21
+ guard();
22
+ assertStorageKeyDeclared({ scope, key, schemas });
23
+ const row = rows.get(key);
24
+ if (!row)
25
+ return null;
26
+ if (isExpired(row)) {
27
+ rows.delete(key);
28
+ return null;
29
+ }
30
+ const data = parseStorageValue({
31
+ scope,
32
+ key,
33
+ operation: 'read',
34
+ schemas,
35
+ value: row.data,
36
+ });
37
+ return {
38
+ data,
39
+ externalId: row.externalId,
40
+ expiresAt: row.expiresAt,
41
+ updatedAt: row.updatedAt,
42
+ };
43
+ },
44
+ async set(key, data, options) {
45
+ var _a;
46
+ guard();
47
+ assertStorageKeyWritable({ scope, key });
48
+ const parsed = parseStorageValue({
49
+ scope,
50
+ key,
51
+ operation: 'write',
52
+ schemas,
53
+ value: data,
54
+ });
55
+ rows.set(key, {
56
+ data: parsed,
57
+ externalId: (_a = options === null || options === void 0 ? void 0 : options.externalId) !== null && _a !== void 0 ? _a : null,
58
+ expiresAt: parseExpiresAt(options),
59
+ updatedAt: new Date().toISOString(),
60
+ });
61
+ },
62
+ async delete(key) {
63
+ guard();
64
+ assertStorageKeyWritable({ scope, key });
65
+ assertStorageKeyDeclared({ scope, key, schemas });
66
+ rows.delete(key);
67
+ },
68
+ };
69
+ }
70
+ function dumpScope(rows) {
71
+ const out = {};
72
+ for (const [key, row] of rows) {
73
+ if (!isExpired(row))
74
+ out[key] = row.data;
75
+ }
76
+ return out;
77
+ }
78
+ export function createMockContext(options) {
79
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
80
+ const userId = options.userId !== undefined ? options.userId : 'mock-user-id';
81
+ const actor = (_a = options.actor) !== null && _a !== void 0 ? _a : (userId !== null
82
+ ? { kind: 'user', userId }
83
+ : { kind: 'machine', tokenId: 'mock-token-id', createdBy: null });
84
+ const schemas = (_b = options.storageSchemas) !== null && _b !== void 0 ? _b : {};
85
+ const accountRows = new Map();
86
+ const userRows = new Map();
87
+ const seedScope = (scope, rows, seed) => {
88
+ for (const [key, value] of Object.entries(seed !== null && seed !== void 0 ? seed : {})) {
89
+ const parsed = parseStorageValue({
90
+ scope,
91
+ key,
92
+ operation: 'write',
93
+ schemas: schemas[scope],
94
+ value,
95
+ });
96
+ rows.set(key, {
97
+ data: parsed,
98
+ externalId: null,
99
+ expiresAt: null,
100
+ updatedAt: new Date().toISOString(),
101
+ });
102
+ }
103
+ };
104
+ seedScope('account', accountRows, (_c = options.storage) === null || _c === void 0 ? void 0 : _c.account);
105
+ seedScope('user', userRows, (_d = options.storage) === null || _d === void 0 ? void 0 : _d.user);
106
+ const guardUserScope = () => {
107
+ if (actor.kind === 'machine') {
108
+ throw new Error('ctx.storage.user is unavailable for machine actors: there is no ' +
109
+ 'user to scope the row to. Store machine-written state in ' +
110
+ 'ctx.storage.account instead.');
111
+ }
112
+ };
113
+ // The stores validate every key and value at runtime via the shared
114
+ // validator; the cast narrows the runtime-checked store to the
115
+ // schema-typed interface the declaration promises.
116
+ const storage = {
117
+ account: createInMemoryStore({
118
+ scope: 'account',
119
+ schemas: schemas.account,
120
+ rows: accountRows,
121
+ guard: () => void 0,
122
+ }),
123
+ user: createInMemoryStore({
124
+ scope: 'user',
125
+ schemas: schemas.user,
126
+ rows: userRows,
127
+ guard: guardUserScope,
128
+ }),
129
+ };
130
+ const accountSecrets = new Map(Object.entries((_f = (_e = options.secrets) === null || _e === void 0 ? void 0 : _e.account) !== null && _f !== void 0 ? _f : {}));
131
+ const adminSecrets = new Map(Object.entries((_h = (_g = options.secrets) === null || _g === void 0 ? void 0 : _g.admin) !== null && _h !== void 0 ? _h : {}));
132
+ const secrets = {
133
+ async get(name) {
134
+ var _a, _b;
135
+ return (_b = (_a = accountSecrets.get(name)) !== null && _a !== void 0 ? _a : adminSecrets.get(name)) !== null && _b !== void 0 ? _b : null;
136
+ },
137
+ async names() {
138
+ return [
139
+ ...new Set([...accountSecrets.keys(), ...adminSecrets.keys()]),
140
+ ].sort();
141
+ },
142
+ async set(name, value) {
143
+ if (!accountSecrets.has(name) && adminSecrets.has(name)) {
144
+ throw new SecretAccessError(name);
145
+ }
146
+ accountSecrets.set(name, value);
147
+ },
148
+ };
149
+ const fetchCalls = [];
150
+ const egress = (_j = options.egress) !== null && _j !== void 0 ? _j : [];
151
+ const fetchHandlers = (_k = options.fetchHandlers) !== null && _k !== void 0 ? _k : [];
152
+ // Not an async function: a denied call must throw synchronously, before
153
+ // any I/O (proposal §2.3) — same behavior the production wrapper has.
154
+ const fetch = (input, init) => {
155
+ const url = new URL(input).toString();
156
+ const allowed = isEgressAllowed(url, egress);
157
+ fetchCalls.push(init === undefined
158
+ ? { url, denied: !allowed }
159
+ : { url, init, denied: !allowed });
160
+ if (!allowed) {
161
+ throw new EgressDeniedError(new URL(url).origin);
162
+ }
163
+ const handler = fetchHandlers.find((candidate) => typeof candidate.match === 'string'
164
+ ? url.startsWith(candidate.match)
165
+ : candidate.match.test(url));
166
+ if (handler) {
167
+ return Promise.resolve(handler.respond(new Request(url, init)));
168
+ }
169
+ return Promise.resolve(new Response('{}', {
170
+ status: 200,
171
+ headers: { 'content-type': 'application/json' },
172
+ }));
173
+ };
174
+ const logs = [];
175
+ const record = (level) => (context, message) => {
176
+ logs.push({ level, context, message });
177
+ };
178
+ const logger = {
179
+ debug: record('debug'),
180
+ info: record('info'),
181
+ warn: record('warn'),
182
+ error: record('error'),
183
+ };
184
+ return {
185
+ accountId: (_l = options.accountId) !== null && _l !== void 0 ? _l : 'mock-account-id',
186
+ accountSlug: (_m = options.accountSlug) !== null && _m !== void 0 ? _m : null,
187
+ userId: actor.kind === 'machine' ? null : userId,
188
+ actor,
189
+ sourceId: (_o = options.sourceId) !== null && _o !== void 0 ? _o : null,
190
+ integration: {
191
+ slug: options.integration.slug,
192
+ productId: (_p = options.integration.productId) !== null && _p !== void 0 ? _p : 'mock-product-id',
193
+ },
194
+ timezone: (_q = options.timezone) !== null && _q !== void 0 ? _q : null,
195
+ storage,
196
+ secrets,
197
+ fetch,
198
+ logger,
199
+ fetchCalls,
200
+ logs,
201
+ dumpStorage() {
202
+ return { account: dumpScope(accountRows), user: dumpScope(userRows) };
203
+ },
204
+ };
205
+ }
206
+ //# sourceMappingURL=mock-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-context.js","sourceRoot":"","sources":["../../src/testing/mock-context.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `createMockContext()` — the day-1 in-memory `IntegrationContext` for the\n * partner harness (proposal §4, amended by rulings 1–4). Self-contained:\n * nothing here touches Supabase, Vault, or the network. Enforcement is the\n * SAME shared implementation the production wrapper uses\n * (`@ekanos/sdk/context`): schema validation via `parseStorageValue`, egress\n * via `isEgressAllowed` — a test that passes the mock cannot be a schema or\n * egress surprise later.\n */\nimport type { z } from 'zod';\n\nimport { isEgressAllowed } from '../context/egress';\nimport { EgressDeniedError, SecretAccessError } from '../context/errors';\nimport {\n assertStorageKeyDeclared,\n assertStorageKeyWritable,\n parseStorageValue,\n} from '../context/storage-validation';\nimport type {\n IntegrationActor,\n IntegrationContext,\n IntegrationFetch,\n IntegrationLogger,\n IntegrationSecrets,\n IntegrationStorage,\n ScopedStore,\n StorageEntry,\n StorageKeySchema,\n StorageSchemaMap,\n StorageSchemas,\n StorageWriteOptions,\n} from '../context/types';\n\ntype StorageScope = 'account' | 'user';\n\n// Seeds are typed off the DECLARED schema, resolved out of whichever\n// declaration form the key used — a bare zod schema or a\n// `{ schema, clientReadable }` descriptor.\ntype ScopeSeed<M> = M extends StorageSchemaMap\n ? { [K in keyof M & string]?: z.input<StorageKeySchema<M[K]>> }\n : Record<never, never>;\n\nexport interface MockFetchHandler {\n /** A string matches URLs that start with it; a RegExp is tested against the full URL. */\n match: string | RegExp;\n respond: (request: Request) => Response | Promise<Response>;\n}\n\nexport interface MockContextOptions<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n accountId?: string;\n accountSlug?: string | null;\n userId?: string | null;\n actor?: IntegrationActor;\n sourceId?: string | null;\n timezone?: string | null;\n integration: { slug: string; productId?: string };\n /**\n * The storage declaration `defineIntegration({ storage })` will carry —\n * the mock enforces the identical schemas via the same shared validator\n * (ruling 1). Omit it and every storage access throws.\n */\n storageSchemas?: Schemas;\n /** Seed rows: key → data, per scope. Validated on construction via the write path. */\n storage?: {\n account?: ScopeSeed<Schemas['account']>;\n user?: ScopeSeed<Schemas['user']>;\n };\n /**\n * Seed secrets, by tier (ruling 2): `account` values are the rows this\n * integration stored — writable via `secrets.set()`; `admin` values stand\n * in for admin-issued source/global-tier credentials — readable, and a\n * `set()` against a name resolvable only there throws `SecretAccessError`.\n */\n secrets?: {\n account?: Record<string, string>;\n admin?: Record<string, string>;\n };\n /** Same allowlist the manifest would declare — enforced by the stub. */\n egress?: readonly string[];\n /** Programmable responses; unmatched allowed calls resolve 200 `{}`. */\n fetchHandlers?: MockFetchHandler[];\n}\n\nexport interface RecordedFetchCall {\n url: string;\n init?: RequestInit;\n denied: boolean;\n}\n\nexport interface RecordedLog {\n level: 'debug' | 'info' | 'warn' | 'error';\n context: Record<string, unknown>;\n message: string;\n}\n\nexport interface MockIntegrationContext<\n Schemas extends StorageSchemas = StorageSchemas,\n> extends IntegrationContext<Schemas> {\n /** Every ctx.fetch call, allowed or denied, in order. */\n readonly fetchCalls: readonly RecordedFetchCall[];\n /** Every log line, per level. */\n readonly logs: readonly RecordedLog[];\n /** Current readable storage state (expired entries excluded), key → data. */\n dumpStorage(): {\n account: Record<string, unknown>;\n user: Record<string, unknown>;\n };\n}\n\ninterface StoredRow {\n data: unknown;\n externalId: string | null;\n expiresAt: string | null;\n updatedAt: string;\n}\n\nfunction isExpired(row: StoredRow): boolean {\n return row.expiresAt !== null && Date.parse(row.expiresAt) <= Date.now();\n}\n\nfunction parseExpiresAt(\n options: StorageWriteOptions | undefined,\n): string | null {\n const expiresAt = options?.expiresAt;\n if (expiresAt === undefined) return null;\n if (Number.isNaN(Date.parse(expiresAt))) {\n throw new TypeError(\n `expiresAt \"${expiresAt}\" is not a parseable timestamp. Pass an ` +\n `ISO-8601 string, e.g. new Date(Date.now() + 60_000).toISOString().`,\n );\n }\n return expiresAt;\n}\n\nfunction createInMemoryStore(args: {\n scope: StorageScope;\n schemas: StorageSchemaMap | undefined;\n rows: Map<string, StoredRow>;\n guard: () => void;\n}): ScopedStore {\n const { scope, schemas, rows, guard } = args;\n\n return {\n async get(key: string): Promise<StorageEntry<unknown> | null> {\n guard();\n assertStorageKeyDeclared({ scope, key, schemas });\n const row = rows.get(key);\n if (!row) return null;\n if (isExpired(row)) {\n rows.delete(key);\n return null;\n }\n const data = parseStorageValue({\n scope,\n key,\n operation: 'read',\n schemas,\n value: row.data,\n });\n return {\n data,\n externalId: row.externalId,\n expiresAt: row.expiresAt,\n updatedAt: row.updatedAt,\n };\n },\n\n async set(\n key: string,\n data: unknown,\n options?: StorageWriteOptions,\n ): Promise<void> {\n guard();\n assertStorageKeyWritable({ scope, key });\n const parsed = parseStorageValue({\n scope,\n key,\n operation: 'write',\n schemas,\n value: data,\n });\n rows.set(key, {\n data: parsed,\n externalId: options?.externalId ?? null,\n expiresAt: parseExpiresAt(options),\n updatedAt: new Date().toISOString(),\n });\n },\n\n async delete(key: string): Promise<void> {\n guard();\n assertStorageKeyWritable({ scope, key });\n assertStorageKeyDeclared({ scope, key, schemas });\n rows.delete(key);\n },\n };\n}\n\nfunction dumpScope(rows: Map<string, StoredRow>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, row] of rows) {\n if (!isExpired(row)) out[key] = row.data;\n }\n return out;\n}\n\nexport function createMockContext<\n Schemas extends StorageSchemas = StorageSchemas,\n>(options: MockContextOptions<Schemas>): MockIntegrationContext<Schemas> {\n const userId = options.userId !== undefined ? options.userId : 'mock-user-id';\n const actor: IntegrationActor =\n options.actor ??\n (userId !== null\n ? { kind: 'user', userId }\n : { kind: 'machine', tokenId: 'mock-token-id', createdBy: null });\n\n const schemas: StorageSchemas = options.storageSchemas ?? {};\n\n const accountRows = new Map<string, StoredRow>();\n const userRows = new Map<string, StoredRow>();\n\n const seedScope = (\n scope: StorageScope,\n rows: Map<string, StoredRow>,\n seed: Record<string, unknown> | undefined,\n ) => {\n for (const [key, value] of Object.entries(seed ?? {})) {\n const parsed = parseStorageValue({\n scope,\n key,\n operation: 'write',\n schemas: schemas[scope],\n value,\n });\n rows.set(key, {\n data: parsed,\n externalId: null,\n expiresAt: null,\n updatedAt: new Date().toISOString(),\n });\n }\n };\n seedScope('account', accountRows, options.storage?.account);\n seedScope('user', userRows, options.storage?.user);\n\n const guardUserScope = () => {\n if (actor.kind === 'machine') {\n throw new Error(\n 'ctx.storage.user is unavailable for machine actors: there is no ' +\n 'user to scope the row to. Store machine-written state in ' +\n 'ctx.storage.account instead.',\n );\n }\n };\n\n // The stores validate every key and value at runtime via the shared\n // validator; the cast narrows the runtime-checked store to the\n // schema-typed interface the declaration promises.\n const storage = {\n account: createInMemoryStore({\n scope: 'account',\n schemas: schemas.account,\n rows: accountRows,\n guard: () => void 0,\n }),\n user: createInMemoryStore({\n scope: 'user',\n schemas: schemas.user,\n rows: userRows,\n guard: guardUserScope,\n }),\n } as unknown as IntegrationStorage<Schemas>;\n\n const accountSecrets = new Map(\n Object.entries(options.secrets?.account ?? {}),\n );\n const adminSecrets = new Map(Object.entries(options.secrets?.admin ?? {}));\n\n const secrets: IntegrationSecrets = {\n async get(name) {\n return accountSecrets.get(name) ?? adminSecrets.get(name) ?? null;\n },\n async names() {\n return [\n ...new Set([...accountSecrets.keys(), ...adminSecrets.keys()]),\n ].sort();\n },\n async set(name, value) {\n if (!accountSecrets.has(name) && adminSecrets.has(name)) {\n throw new SecretAccessError(name);\n }\n accountSecrets.set(name, value);\n },\n };\n\n const fetchCalls: RecordedFetchCall[] = [];\n const egress = options.egress ?? [];\n const fetchHandlers = options.fetchHandlers ?? [];\n\n // Not an async function: a denied call must throw synchronously, before\n // any I/O (proposal §2.3) — same behavior the production wrapper has.\n const fetch: IntegrationFetch = (input, init) => {\n const url = new URL(input).toString();\n const allowed = isEgressAllowed(url, egress);\n fetchCalls.push(\n init === undefined\n ? { url, denied: !allowed }\n : { url, init, denied: !allowed },\n );\n if (!allowed) {\n throw new EgressDeniedError(new URL(url).origin);\n }\n const handler = fetchHandlers.find((candidate) =>\n typeof candidate.match === 'string'\n ? url.startsWith(candidate.match)\n : candidate.match.test(url),\n );\n if (handler) {\n return Promise.resolve(handler.respond(new Request(url, init)));\n }\n return Promise.resolve(\n new Response('{}', {\n status: 200,\n headers: { 'content-type': 'application/json' },\n }),\n );\n };\n\n const logs: RecordedLog[] = [];\n const record =\n (level: RecordedLog['level']) =>\n (context: Record<string, unknown>, message: string) => {\n logs.push({ level, context, message });\n };\n const logger: IntegrationLogger = {\n debug: record('debug'),\n info: record('info'),\n warn: record('warn'),\n error: record('error'),\n };\n\n return {\n accountId: options.accountId ?? 'mock-account-id',\n accountSlug: options.accountSlug ?? null,\n userId: actor.kind === 'machine' ? null : userId,\n actor,\n sourceId: options.sourceId ?? null,\n integration: {\n slug: options.integration.slug,\n productId: options.integration.productId ?? 'mock-product-id',\n },\n timezone: options.timezone ?? null,\n storage,\n secrets,\n fetch,\n logger,\n fetchCalls,\n logs,\n dumpStorage() {\n return { account: dumpScope(accountRows), user: dumpScope(userRows) };\n },\n };\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import type { CSSProperties, ComponentType } from 'react';
2
+ /**
3
+ * The shape an integration-declared icon renders through, defined locally so
4
+ * the SDK's root entrypoint imports nothing from the host. `@kit/ui/icon`
5
+ * owns the canonical definition (and the icon-name union lives in the host
6
+ * design system, which validates icon names at gate time); to the SDK an icon
7
+ * is just a component taking presentation props.
8
+ */
9
+ export type IconRenderer = ComponentType<{
10
+ className?: string;
11
+ style?: CSSProperties;
12
+ }>;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=icon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icon.js","sourceRoot":"","sources":["../../src/types/icon.ts"],"names":[],"mappings":"","sourcesContent":["import type { CSSProperties, ComponentType } from 'react';\n\n/**\n * The shape an integration-declared icon renders through, defined locally so\n * the SDK's root entrypoint imports nothing from the host. `@kit/ui/icon`\n * owns the canonical definition (and the icon-name union lives in the host\n * design system, which validates icon names at gate time); to the SDK an icon\n * is just a component taking presentation props.\n */\nexport type IconRenderer = ComponentType<{\n className?: string;\n style?: CSSProperties;\n}>;\n"]}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * SDK-owned type definitions (adversarial review outcome 5,
3
+ * docs/devex/sdk-export-map.md): the canonical partner-facing shapes live
4
+ * here, import nothing from `@kit/*`, and are held mutually assignable with
5
+ * `@kit/integrations-core`'s copies by `__tests__/core-parity.test-d.ts`.
6
+ */
7
+ export type { IconRenderer } from './icon.js';
8
+ export type { AccountProductData, AccountProductDataType, ActivationFormProps, ActivationResult, HealthCheckResult, HealthStatus, IntegrationCapability, IntegrationComponentProps, IntegrationConfig, IntegrationMetadata, IntegrationPermission, MarketplaceTileProps, WidgetConfig, } from './integration.js';
9
+ export type { WidgetAskContext, WidgetSnapshot, WidgetSuggestedPrompt, } from './widget-ask-context.js';
10
+ export type { WorkspaceTargetDefinition } from './workspace-target.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * SDK-owned type definitions (adversarial review outcome 5,
3
+ * docs/devex/sdk-export-map.md): the canonical partner-facing shapes live
4
+ * here, import nothing from `@kit/*`, and are held mutually assignable with
5
+ * `@kit/integrations-core`'s copies by `__tests__/core-parity.test-d.ts`.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG","sourcesContent":["/**\n * SDK-owned type definitions (adversarial review outcome 5,\n * docs/devex/sdk-export-map.md): the canonical partner-facing shapes live\n * here, import nothing from `@kit/*`, and are held mutually assignable with\n * `@kit/integrations-core`'s copies by `__tests__/core-parity.test-d.ts`.\n */\n\nexport type { IconRenderer } from './icon';\n\nexport type {\n AccountProductData,\n AccountProductDataType,\n ActivationFormProps,\n ActivationResult,\n HealthCheckResult,\n HealthStatus,\n IntegrationCapability,\n IntegrationComponentProps,\n IntegrationConfig,\n IntegrationMetadata,\n IntegrationPermission,\n MarketplaceTileProps,\n WidgetConfig,\n} from './integration';\n\nexport type {\n WidgetAskContext,\n WidgetSnapshot,\n WidgetSuggestedPrompt,\n} from './widget-ask-context';\n\nexport type { WorkspaceTargetDefinition } from './workspace-target';\n"]}