@ekanos/integration-schema 0.1.2 → 0.1.4

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 CHANGED
@@ -10,19 +10,25 @@ a dependency cycle and there is no hand-written structural twin to drift.
10
10
 
11
11
  ## Why it exists
12
12
 
13
- Adversarial review (GPT-5.6, 2026-08-27) found three problems a shared schema
14
- resolves at once:
13
+ A security review found three problems a shared schema resolves at once:
15
14
 
16
- - **F3** — the host must re-parse partner exports at the trust boundary, not
15
+ - The host must re-parse partner exports at the trust boundary rather than
17
16
  trust a `.d.ts`. `registerPartnerIntegration()` calls
18
- `parseIntegrationDefinition()` from here.
19
- - **F9** hand-written structural twins in `@kit/integrations-core` could not
20
- catch security-relevant drift. There are no twins now: one type, imported by
21
- both sides.
22
- - **F10** the SDK validated a weaker workspace contract than the host. The
17
+ `parseIntegrationDefinition()` from here, so a definition is validated twice
18
+ against the same rules: once when you author it, once when the host accepts
19
+ it.
20
+ - Hand-written structural twins on the host side could not catch
21
+ security-relevant drift. There are no twins now: one type, imported by both
22
+ sides.
23
+ - The SDK used to validate a weaker workspace contract than the host. The
23
24
  workspace-target schema lives here; the host injects icon-name validity via
24
25
  `createWorkspaceTargetListSchema({ isValidIcon })`.
25
26
 
27
+ **Authoring an integration? Read the `@ekanos/sdk` README instead.** It is the
28
+ partner guide, and it documents every field this schema validates with a worked
29
+ example. This package is the contract underneath it: you install it only if you
30
+ need the schema or the types without the SDK.
31
+
26
32
  ## What's here
27
33
 
28
34
  - `IntegrationDefinitionSchema` — the strict canonical zod schema.
@@ -40,9 +46,10 @@ Dependency-pure by construction: `zod` only. No `@kit/*`, no React, no
40
46
 
41
47
  ## Publishing
42
48
 
43
- This package is publishable (`version` set, not `private`) and is a **runtime
44
- dependency of `@ekanos/sdk`**, so it must be published **before** the SDK — a
45
- fresh `npm install @ekanos/sdk` resolves `@ekanos/integration-schema` from the
46
- registry. The compiled ESM in `dist/` uses explicit `.js` relative specifiers
49
+ This package is a **runtime dependency of `@ekanos/sdk`**, so it must be
50
+ published **before** the SDK — a fresh `npm install @ekanos/sdk` resolves
51
+ `@ekanos/integration-schema` from the registry. (It carries `private: true`
52
+ between releases as a deliberate publish latch; a release commit lifts it on
53
+ every publishable package at once.) The compiled ESM in `dist/` uses explicit `.js` relative specifiers
47
54
  so a real Node import resolves without a bundler; the SDK's `pack:test`
48
55
  exercises that path end to end.
@@ -7,7 +7,7 @@
7
7
  * zod-type-only, erased at compile time. The value half of the contract —
8
8
  * error classes, the egress matcher, the storage validators — lives in
9
9
  * `@ekanos/sdk/context`; the in-memory mock in `@ekanos/sdk/testing`.
10
- * Spec: docs/devex/capability-context-proposal.md (all six §7 rulings).
10
+ * The authoring guide for everything below is the `@ekanos/sdk` README.
11
11
  */
12
12
  import type { z } from 'zod';
13
13
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"capability-context.js","sourceRoot":"","sources":["../src/capability-context.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The capability context — the one object partner code receives.\n *\n * Canonical home (moved here from `@ekanos/sdk`'s `context/types.ts` so the\n * definition schema below can reference `IntegrationContext` without a\n * package cycle; `@ekanos/sdk` re-exports every type from here). Pure types,\n * zod-type-only, erased at compile time. The value half of the contract —\n * error classes, the egress matcher, the storage validators — lives in\n * `@ekanos/sdk/context`; the in-memory mock in `@ekanos/sdk/testing`.\n * Spec: docs/devex/capability-context-proposal.md (all six §7 rulings).\n */\nimport type { z } from 'zod';\n\n/**\n * Who is executing. A closed union: a machine context without a grant is\n * unrepresentable rather than merely checked (proposal §2).\n */\nexport type IntegrationActor =\n | { readonly kind: 'user'; readonly userId: string }\n | {\n readonly kind: 'machine';\n readonly tokenId: string;\n readonly createdBy: string | null;\n };\n\n/**\n * One storage key's declaration in EXPLICIT form: the zod schema plus the\n * exposure flags that decide which host surfaces may serve the key.\n *\n * Exposure is opt-in and fails closed. A declaration's keys are server-side\n * by construction — partner code reaches them only through `ctx.storage`,\n * which runs behind the capability layer. The generic browser-readable\n * storage route (`GET /api/integrations/[slug]/storage`) would otherwise\n * turn EVERY declared key into a browser-readable one, including keys a\n * partner reasonably treated as server-only: sync cursors, cached upstream\n * responses, internal bookkeeping. So the route serves a key only when its\n * declaration opts in with `clientReadable: true`.\n */\nexport interface StorageKeyDeclaration<S extends z.ZodType = z.ZodType> {\n readonly schema: S;\n /**\n * Opt in to the generic browser-readable storage route. **Defaults to\n * `false`** — an omitted flag, and the bare-schema declaration form, both\n * mean server-only. Set it only for values the browser legitimately needs\n * and that are safe for any active member of the account to read (widget\n * settings, display preferences). Never for cursors, cached upstream\n * payloads, or anything a `secret` would be a better home for.\n */\n readonly clientReadable?: boolean;\n}\n\n/**\n * How a single storage key may be declared: a bare zod schema (the original\n * form — always server-only), or a `StorageKeyDeclaration` descriptor that\n * carries exposure flags alongside the schema. Both forms validate reads and\n * writes identically; only the descriptor can widen exposure.\n */\nexport type StorageKeyDeclarationInput<S extends z.ZodType = z.ZodType> =\n | S\n | StorageKeyDeclaration<S>;\n\n/**\n * Storage schemas for one scope: key → zod schema, or key → declaration\n * descriptor (ruling 1). Keys are strict — a key with no declared schema\n * cannot be read, written, or deleted; the attempt is a type error where the\n * schema map is statically known, and always a runtime\n * `StorageValidationError`.\n */\nexport type StorageSchemaMap = Record<string, StorageKeyDeclarationInput>;\n\n/**\n * Resolves the zod schema out of either declaration form, so `ScopedStore`\n * stays schema-typed whichever form the author chose.\n */\nexport type StorageKeySchema<D> = [D] extends [z.ZodType]\n ? D\n : D extends StorageKeyDeclaration<infer S>\n ? S\n : D extends z.ZodType\n ? D\n : never;\n\n/**\n * The storage declaration `defineIntegration({ storage })` consumes and\n * `createMockContext({ storageSchemas })` enforces (ruling 1). A scope left\n * undeclared has no usable keys.\n */\nexport interface StorageSchemas {\n account?: StorageSchemaMap;\n user?: StorageSchemaMap;\n}\n\n/**\n * Resolves the schema map for one scope of a declaration. A scope the\n * integration did not declare resolves to an empty map (no key typechecks);\n * only the unparameterized default (`StorageSchemas` itself) stays\n * permissive, for signatures that cannot know the integration's schemas.\n */\nexport type StorageScopeSchemas<\n Schemas extends StorageSchemas,\n Scope extends keyof StorageSchemas,\n> = [Schemas[Scope]] extends [undefined]\n ? Record<never, never>\n : NonNullable<Schemas[Scope]>;\n\nexport interface StorageEntry<T> {\n data: T;\n externalId: string | null;\n expiresAt: string | null;\n updatedAt: string;\n}\n\nexport interface StorageWriteOptions {\n externalId?: string;\n /** ISO-8601 timestamp; an entry past it reads as `null`. */\n expiresAt?: string;\n}\n\n/**\n * One scope of `ctx.storage`, bound to `{account, product}` (and `userId`\n * for the user scope) at construction — there is no id parameter to lie in.\n *\n * Schema-typed (ruling 1): `get` returns the declared schema's output type,\n * `set` validates before write, and a read whose stored data no longer\n * matches the schema throws `StorageValidationError` — schema evolution is\n * handled explicitly (versioned keys or `z.union`), never silently.\n * All methods are async (ruling 4).\n */\nexport interface ScopedStore<\n Schemas extends StorageSchemaMap = StorageSchemaMap,\n> {\n get<K extends keyof Schemas & string>(\n key: K,\n ): Promise<StorageEntry<z.output<StorageKeySchema<Schemas[K]>>> | null>;\n set<K extends keyof Schemas & string>(\n key: K,\n data: z.input<StorageKeySchema<Schemas[K]>>,\n options?: StorageWriteOptions,\n ): Promise<void>;\n delete<K extends keyof Schemas & string>(key: K): Promise<void>;\n}\n\nexport interface IntegrationStorage<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n /** account_product_data, bound to {accountId, productId}. */\n readonly account: ScopedStore<StorageScopeSchemas<Schemas, 'account'>>;\n /**\n * user_product_data, bound to {userId, accountId, productId}.\n * Throws for machine actors.\n */\n readonly user: ScopedStore<StorageScopeSchemas<Schemas, 'user'>>;\n}\n\n/**\n * Reads from the credential set the host resolved (account → source →\n * global tiers + hydrated activation secrets) before partner code ran.\n * All methods are async (ruling 4).\n */\nexport interface IntegrationSecrets {\n /** A named field of the resolved credential set + hydrated activation secrets. */\n get(name: string): Promise<string | null>;\n /** All resolved secret names (values not included) — for capability probing. */\n names(): Promise<string[]>;\n /**\n * Write back a rotated value (OAuth refresh). Tier-bound (ruling 2):\n * writes go only to the account-tier store bound to {account, product} at\n * construction. Source- and global-tier credentials (admin-issued) are\n * structurally unreachable from this path; a write against a name that\n * resolves only from those tiers throws `SecretAccessError`.\n */\n set(name: string, value: string): Promise<void>;\n}\n\n/**\n * Host-allowlisted egress (ruling 3): origin-only allowlist with explicit\n * `*.` subdomain wildcards, enforced against the RESOLVED URL's origin —\n * redirect targets included. A denied call throws `EgressDeniedError`\n * synchronously, before any network I/O.\n */\nexport type IntegrationFetch = (\n input: string | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\n/**\n * Structured, pino-style. The host pre-binds `{integration, accountId,\n * actor}` so every partner log line is attributable.\n */\nexport interface IntegrationLogger {\n debug(context: Record<string, unknown>, message: string): void;\n info(context: Record<string, unknown>, message: string): void;\n warn(context: Record<string, unknown>, message: string): void;\n error(context: Record<string, unknown>, message: string): void;\n}\n\n/**\n * One host-constructed object whose every method is already scoped to\n * `{account, integration}` before partner code runs. Handed to MCP tool\n * `run()` and SDK server handlers alike (ruling 5: one type, one mock).\n */\nexport interface IntegrationContext<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n /** Identity facts — read-only, informational. Authorization already happened. */\n readonly accountId: string;\n readonly accountSlug: string | null;\n readonly userId: string | null; // null for machine actors\n readonly actor: IntegrationActor;\n readonly sourceId: string | null;\n readonly integration: { readonly slug: string; readonly productId: string };\n readonly timezone: string | null;\n\n readonly storage: IntegrationStorage<Schemas>;\n readonly secrets: IntegrationSecrets;\n readonly fetch: IntegrationFetch;\n readonly logger: IntegrationLogger;\n}\n"]}
1
+ {"version":3,"file":"capability-context.js","sourceRoot":"","sources":["../src/capability-context.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The capability context — the one object partner code receives.\n *\n * Canonical home (moved here from `@ekanos/sdk`'s `context/types.ts` so the\n * definition schema below can reference `IntegrationContext` without a\n * package cycle; `@ekanos/sdk` re-exports every type from here). Pure types,\n * zod-type-only, erased at compile time. The value half of the contract —\n * error classes, the egress matcher, the storage validators — lives in\n * `@ekanos/sdk/context`; the in-memory mock in `@ekanos/sdk/testing`.\n * The authoring guide for everything below is the `@ekanos/sdk` README.\n */\nimport type { z } from 'zod';\n\n/**\n * Who is executing. A closed union: a machine context without a grant is\n * unrepresentable rather than merely checked (proposal §2).\n */\nexport type IntegrationActor =\n | { readonly kind: 'user'; readonly userId: string }\n | {\n readonly kind: 'machine';\n readonly tokenId: string;\n readonly createdBy: string | null;\n };\n\n/**\n * One storage key's declaration in EXPLICIT form: the zod schema plus the\n * exposure flags that decide which host surfaces may serve the key.\n *\n * Exposure is opt-in and fails closed. A declaration's keys are server-side\n * by construction — partner code reaches them only through `ctx.storage`,\n * which runs behind the capability layer. The generic browser-readable\n * storage route (`GET /api/integrations/[slug]/storage`) would otherwise\n * turn EVERY declared key into a browser-readable one, including keys a\n * partner reasonably treated as server-only: sync cursors, cached upstream\n * responses, internal bookkeeping. So the route serves a key only when its\n * declaration opts in with `clientReadable: true`.\n */\nexport interface StorageKeyDeclaration<S extends z.ZodType = z.ZodType> {\n readonly schema: S;\n /**\n * Opt in to the generic browser-readable storage route. **Defaults to\n * `false`** — an omitted flag, and the bare-schema declaration form, both\n * mean server-only. Set it only for values the browser legitimately needs\n * and that are safe for any active member of the account to read (widget\n * settings, display preferences). Never for cursors, cached upstream\n * payloads, or anything a `secret` would be a better home for.\n */\n readonly clientReadable?: boolean;\n}\n\n/**\n * How a single storage key may be declared: a bare zod schema (the original\n * form — always server-only), or a `StorageKeyDeclaration` descriptor that\n * carries exposure flags alongside the schema. Both forms validate reads and\n * writes identically; only the descriptor can widen exposure.\n */\nexport type StorageKeyDeclarationInput<S extends z.ZodType = z.ZodType> =\n | S\n | StorageKeyDeclaration<S>;\n\n/**\n * Storage schemas for one scope: key → zod schema, or key → declaration\n * descriptor (ruling 1). Keys are strict — a key with no declared schema\n * cannot be read, written, or deleted; the attempt is a type error where the\n * schema map is statically known, and always a runtime\n * `StorageValidationError`.\n */\nexport type StorageSchemaMap = Record<string, StorageKeyDeclarationInput>;\n\n/**\n * Resolves the zod schema out of either declaration form, so `ScopedStore`\n * stays schema-typed whichever form the author chose.\n */\nexport type StorageKeySchema<D> = [D] extends [z.ZodType]\n ? D\n : D extends StorageKeyDeclaration<infer S>\n ? S\n : D extends z.ZodType\n ? D\n : never;\n\n/**\n * The storage declaration `defineIntegration({ storage })` consumes and\n * `createMockContext({ storageSchemas })` enforces (ruling 1). A scope left\n * undeclared has no usable keys.\n */\nexport interface StorageSchemas {\n account?: StorageSchemaMap;\n user?: StorageSchemaMap;\n}\n\n/**\n * Resolves the schema map for one scope of a declaration. A scope the\n * integration did not declare resolves to an empty map (no key typechecks);\n * only the unparameterized default (`StorageSchemas` itself) stays\n * permissive, for signatures that cannot know the integration's schemas.\n */\nexport type StorageScopeSchemas<\n Schemas extends StorageSchemas,\n Scope extends keyof StorageSchemas,\n> = [Schemas[Scope]] extends [undefined]\n ? Record<never, never>\n : NonNullable<Schemas[Scope]>;\n\nexport interface StorageEntry<T> {\n data: T;\n externalId: string | null;\n expiresAt: string | null;\n updatedAt: string;\n}\n\nexport interface StorageWriteOptions {\n externalId?: string;\n /** ISO-8601 timestamp; an entry past it reads as `null`. */\n expiresAt?: string;\n}\n\n/**\n * One scope of `ctx.storage`, bound to `{account, product}` (and `userId`\n * for the user scope) at construction — there is no id parameter to lie in.\n *\n * Schema-typed (ruling 1): `get` returns the declared schema's output type,\n * `set` validates before write, and a read whose stored data no longer\n * matches the schema throws `StorageValidationError` — schema evolution is\n * handled explicitly (versioned keys or `z.union`), never silently.\n * All methods are async (ruling 4).\n */\nexport interface ScopedStore<\n Schemas extends StorageSchemaMap = StorageSchemaMap,\n> {\n get<K extends keyof Schemas & string>(\n key: K,\n ): Promise<StorageEntry<z.output<StorageKeySchema<Schemas[K]>>> | null>;\n set<K extends keyof Schemas & string>(\n key: K,\n data: z.input<StorageKeySchema<Schemas[K]>>,\n options?: StorageWriteOptions,\n ): Promise<void>;\n delete<K extends keyof Schemas & string>(key: K): Promise<void>;\n}\n\nexport interface IntegrationStorage<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n /** account_product_data, bound to {accountId, productId}. */\n readonly account: ScopedStore<StorageScopeSchemas<Schemas, 'account'>>;\n /**\n * user_product_data, bound to {userId, accountId, productId}.\n * Throws for machine actors.\n */\n readonly user: ScopedStore<StorageScopeSchemas<Schemas, 'user'>>;\n}\n\n/**\n * Reads from the credential set the host resolved (account → source →\n * global tiers + hydrated activation secrets) before partner code ran.\n * All methods are async (ruling 4).\n */\nexport interface IntegrationSecrets {\n /** A named field of the resolved credential set + hydrated activation secrets. */\n get(name: string): Promise<string | null>;\n /** All resolved secret names (values not included) — for capability probing. */\n names(): Promise<string[]>;\n /**\n * Write back a rotated value (OAuth refresh). Tier-bound (ruling 2):\n * writes go only to the account-tier store bound to {account, product} at\n * construction. Source- and global-tier credentials (admin-issued) are\n * structurally unreachable from this path; a write against a name that\n * resolves only from those tiers throws `SecretAccessError`.\n */\n set(name: string, value: string): Promise<void>;\n}\n\n/**\n * Host-allowlisted egress (ruling 3): origin-only allowlist with explicit\n * `*.` subdomain wildcards, enforced against the RESOLVED URL's origin —\n * redirect targets included. A denied call throws `EgressDeniedError`\n * synchronously, before any network I/O.\n */\nexport type IntegrationFetch = (\n input: string | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\n/**\n * Structured, pino-style. The host pre-binds `{integration, accountId,\n * actor}` so every partner log line is attributable.\n */\nexport interface IntegrationLogger {\n debug(context: Record<string, unknown>, message: string): void;\n info(context: Record<string, unknown>, message: string): void;\n warn(context: Record<string, unknown>, message: string): void;\n error(context: Record<string, unknown>, message: string): void;\n}\n\n/**\n * One host-constructed object whose every method is already scoped to\n * `{account, integration}` before partner code runs. Handed to MCP tool\n * `run()` and SDK server handlers alike (ruling 5: one type, one mock).\n */\nexport interface IntegrationContext<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n /** Identity facts — read-only, informational. Authorization already happened. */\n readonly accountId: string;\n readonly accountSlug: string | null;\n readonly userId: string | null; // null for machine actors\n readonly actor: IntegrationActor;\n readonly sourceId: string | null;\n readonly integration: { readonly slug: string; readonly productId: string };\n readonly timezone: string | null;\n\n readonly storage: IntegrationStorage<Schemas>;\n readonly secrets: IntegrationSecrets;\n readonly fetch: IntegrationFetch;\n readonly logger: IntegrationLogger;\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * Relative specifiers are extensionless here on purpose. Workspace consumers
14
14
  * resolve this package's raw `src/*.ts` through a bundler, so a `./x.js`
15
15
  * specifier would point at a file that does not exist. The published ESM still
16
- * needs the extension for a real Node import, so `scripts/rewrite-esm-specifiers.mjs`
16
+ * needs the extension for a real Node import, so the build's specifier-rewrite step
17
17
  * adds it to `dist/` after the compiler runs — see that file for the full
18
18
  * rationale. Do not hand-write `.js` extensions back into this source tree.
19
19
  */
@@ -22,6 +22,6 @@ export type { ComponentReference } from './component-reference.js';
22
22
  export { isComponentReference } from './component-reference.js';
23
23
  export { DNS_NAMESPACE, EKANOS_INTEGRATION_NAMESPACE, derivePlanId, deriveProductId, uuidv5, } from './product-id.js';
24
24
  export { WorkspaceTargetSchema, WorkspaceTargetListSchema, WorkspaceTargetSlugSchema, WorkspaceTargetLayoutSchema, createWorkspaceTargetSchema, createWorkspaceTargetListSchema, normalizeWorkspaceTargets, type WorkspaceTargetSchemaOptions, type WorkspaceTargetDefinition, type ResolvedWorkspaceTarget, } from './workspace-target.js';
25
- export { IntegrationDefinitionSchema, parseIntegrationDefinition, validateIntegrationDefinitions, collectDefinitionFindings, collectCollisionFindings, getDiscoveredToolName, assertPlainDeclaration, deepFreezeDefinition, } from './integration-definition.js';
25
+ export { IntegrationDefinitionSchema, parseIntegrationDefinition, validateIntegrationDefinitions, collectDefinitionFindings, collectCollisionFindings, getDiscoveredToolName, assertPlainDeclaration, deepFreezeDefinition, ACCOUNT_STORAGE_DATA_TYPES, USER_STORAGE_DATA_TYPES, RESERVED_STORAGE_DATA_TYPES, } from './integration-definition.js';
26
26
  export type { Finding, CollectDefinitionOptions, } from './integration-definition.js';
27
- export type { IntegrationDefinition, IntegrationComponentDeclarations, IntegrationProposals, IntegrationCapabilityDeclaration, IntegrationPermissionDeclaration, PartnerWidgetDeclaration, PartnerToolModule, PartnerToolParameters, ToolClassificationProposal, DefinitionCollisionInput, FirstPartyInventory, JsonValue, PartnerWebhookDeclaration, WebhookSignatureDeclaration, WebhookEvent, WebhookResult, PartnerScheduleDeclaration, ScheduleInvocation, ScheduleResult, PartnerOAuthDeclaration, OAuthProviderDeclaration, OAuthTokens, } from './integration-definition.js';
27
+ export type { IntegrationDefinition, IntegrationComponentDeclarations, IntegrationProposals, IntegrationCapabilityDeclaration, IntegrationPermissionDeclaration, PartnerWidgetDeclaration, PartnerToolModule, PartnerToolParameters, ToolClassificationProposal, DefinitionCollisionInput, FirstPartyInventory, JsonValue, PartnerWebhookDeclaration, WebhookSignatureDeclaration, WebhookEvent, WebhookResult, PartnerScheduleDeclaration, ScheduleInvocation, ScheduleResult, PartnerOAuthDeclaration, OAuthProviderDeclaration, OAuthTokens, OnActivateHandler, } from './integration-definition.js';
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * Relative specifiers are extensionless here on purpose. Workspace consumers
14
14
  * resolve this package's raw `src/*.ts` through a bundler, so a `./x.js`
15
15
  * specifier would point at a file that does not exist. The published ESM still
16
- * needs the extension for a real Node import, so `scripts/rewrite-esm-specifiers.mjs`
16
+ * needs the extension for a real Node import, so the build's specifier-rewrite step
17
17
  * adds it to `dist/` after the compiler runs — see that file for the full
18
18
  * rationale. Do not hand-write `.js` extensions back into this source tree.
19
19
  */
@@ -29,5 +29,9 @@ export { IntegrationDefinitionSchema, parseIntegrationDefinition, validateIntegr
29
29
  // Non-throwing sibling collectors (0.2.0 surface add): recover structured
30
30
  // findings instead of a thrown, pre-formatted string. `@ekanos/cli validate`
31
31
  // consumes these; the throwing functions above are built on the same rules.
32
- collectDefinitionFindings, collectCollisionFindings, getDiscoveredToolName, assertPlainDeclaration, deepFreezeDefinition, } from './integration-definition.js';
32
+ collectDefinitionFindings, collectCollisionFindings, getDiscoveredToolName, assertPlainDeclaration, deepFreezeDefinition,
33
+ // The storage data_type allowlists. Exported because the HOST imports them
34
+ // for its runtime `ctx.storage` check — one list, validated at authoring
35
+ // time here and at access time there, so the two cannot drift.
36
+ ACCOUNT_STORAGE_DATA_TYPES, USER_STORAGE_DATA_TYPES, RESERVED_STORAGE_DATA_TYPES, } from './integration-definition.js';
33
37
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAsBH,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE7D,6EAA6E;AAC7E,6EAA6E;AAC7E,kCAAkC;AAClC,OAAO,EACL,aAAa,EACb,4BAA4B,EAC5B,YAAY,EACZ,eAAe,EACf,MAAM,GACP,MAAM,cAAc,CAAC;AAEtB,0EAA0E;AAC1E,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,yBAAyB,EACzB,2BAA2B,EAC3B,2BAA2B,EAC3B,+BAA+B,EAC/B,yBAAyB,GAI1B,MAAM,oBAAoB,CAAC;AAE5B,sCAAsC;AACtC,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,8BAA8B;AAC9B,0EAA0E;AAC1E,6EAA6E;AAC7E,4EAA4E;AAC5E,yBAAyB,EACzB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC","sourcesContent":["/**\n * @ekanos/integration-schema — the canonical, dependency-pure contract for\n * Ekanos integration definitions.\n *\n * ONE zod schema, ONE set of types, imported by BOTH `@ekanos/sdk`\n * (authoring: `defineIntegration()`) and `@kit/integrations-core` (host trust\n * boundary: `registerPartnerIntegration()` re-parses against this schema).\n * This package depends on nothing but zod, so neither consumer forms a cycle\n * and there is no hand-written structural twin to drift (F3/F9/F10).\n *\n * Depends on: zod. Nothing else — no `@kit/*`, no React, no `server-only`.\n *\n * Relative specifiers are extensionless here on purpose. Workspace consumers\n * resolve this package's raw `src/*.ts` through a bundler, so a `./x.js`\n * specifier would point at a file that does not exist. The published ESM still\n * needs the extension for a real Node import, so `scripts/rewrite-esm-specifiers.mjs`\n * adds it to `dist/` after the compiler runs — see that file for the full\n * rationale. Do not hand-write `.js` extensions back into this source tree.\n */\n\n// Capability-context types (canonical home; @ekanos/sdk re-exports these)\nexport type {\n IntegrationActor,\n IntegrationContext,\n IntegrationStorage,\n ScopedStore,\n IntegrationSecrets,\n IntegrationFetch,\n IntegrationLogger,\n StorageEntry,\n StorageWriteOptions,\n StorageSchemas,\n StorageSchemaMap,\n StorageScopeSchemas,\n StorageKeyDeclaration,\n StorageKeyDeclarationInput,\n StorageKeySchema,\n} from './capability-context';\n\nexport type { ComponentReference } from './component-reference';\nexport { isComponentReference } from './component-reference';\n\n// Deterministic slug → uuid derivation. One slug, one product id, everywhere\n// and forever — the SDK, the seed generator and any future gate all agree by\n// computing rather than choosing.\nexport {\n DNS_NAMESPACE,\n EKANOS_INTEGRATION_NAMESPACE,\n derivePlanId,\n deriveProductId,\n uuidv5,\n} from './product-id';\n\n// Workspace-target contract (F10): one schema, host injects icon validity\nexport {\n WorkspaceTargetSchema,\n WorkspaceTargetListSchema,\n WorkspaceTargetSlugSchema,\n WorkspaceTargetLayoutSchema,\n createWorkspaceTargetSchema,\n createWorkspaceTargetListSchema,\n normalizeWorkspaceTargets,\n type WorkspaceTargetSchemaOptions,\n type WorkspaceTargetDefinition,\n type ResolvedWorkspaceTarget,\n} from './workspace-target';\n\n// The integration-definition contract\nexport {\n IntegrationDefinitionSchema,\n parseIntegrationDefinition,\n validateIntegrationDefinitions,\n // Non-throwing sibling collectors (0.2.0 surface add): recover structured\n // findings instead of a thrown, pre-formatted string. `@ekanos/cli validate`\n // consumes these; the throwing functions above are built on the same rules.\n collectDefinitionFindings,\n collectCollisionFindings,\n getDiscoveredToolName,\n assertPlainDeclaration,\n deepFreezeDefinition,\n} from './integration-definition';\n\nexport type {\n Finding,\n CollectDefinitionOptions,\n} from './integration-definition';\n\nexport type {\n IntegrationDefinition,\n IntegrationComponentDeclarations,\n IntegrationProposals,\n IntegrationCapabilityDeclaration,\n IntegrationPermissionDeclaration,\n PartnerWidgetDeclaration,\n PartnerToolModule,\n PartnerToolParameters,\n ToolClassificationProposal,\n DefinitionCollisionInput,\n FirstPartyInventory,\n JsonValue,\n // Event surfaces (webhooks, schedules, OAuth) — interface-first: the local\n // harness executes them today; the host transports bind to them later.\n PartnerWebhookDeclaration,\n WebhookSignatureDeclaration,\n WebhookEvent,\n WebhookResult,\n PartnerScheduleDeclaration,\n ScheduleInvocation,\n ScheduleResult,\n PartnerOAuthDeclaration,\n OAuthProviderDeclaration,\n OAuthTokens,\n} from './integration-definition';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAsBH,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE7D,6EAA6E;AAC7E,6EAA6E;AAC7E,kCAAkC;AAClC,OAAO,EACL,aAAa,EACb,4BAA4B,EAC5B,YAAY,EACZ,eAAe,EACf,MAAM,GACP,MAAM,cAAc,CAAC;AAEtB,0EAA0E;AAC1E,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,yBAAyB,EACzB,2BAA2B,EAC3B,2BAA2B,EAC3B,+BAA+B,EAC/B,yBAAyB,GAI1B,MAAM,oBAAoB,CAAC;AAE5B,sCAAsC;AACtC,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,8BAA8B;AAC9B,0EAA0E;AAC1E,6EAA6E;AAC7E,4EAA4E;AAC5E,yBAAyB,EACzB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB;AACpB,2EAA2E;AAC3E,yEAAyE;AACzE,+DAA+D;AAC/D,0BAA0B,EAC1B,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,0BAA0B,CAAC","sourcesContent":["/**\n * @ekanos/integration-schema — the canonical, dependency-pure contract for\n * Ekanos integration definitions.\n *\n * ONE zod schema, ONE set of types, imported by BOTH `@ekanos/sdk`\n * (authoring: `defineIntegration()`) and `@kit/integrations-core` (host trust\n * boundary: `registerPartnerIntegration()` re-parses against this schema).\n * This package depends on nothing but zod, so neither consumer forms a cycle\n * and there is no hand-written structural twin to drift (F3/F9/F10).\n *\n * Depends on: zod. Nothing else — no `@kit/*`, no React, no `server-only`.\n *\n * Relative specifiers are extensionless here on purpose. Workspace consumers\n * resolve this package's raw `src/*.ts` through a bundler, so a `./x.js`\n * specifier would point at a file that does not exist. The published ESM still\n * needs the extension for a real Node import, so the build's specifier-rewrite step\n * adds it to `dist/` after the compiler runs — see that file for the full\n * rationale. Do not hand-write `.js` extensions back into this source tree.\n */\n\n// Capability-context types (canonical home; @ekanos/sdk re-exports these)\nexport type {\n IntegrationActor,\n IntegrationContext,\n IntegrationStorage,\n ScopedStore,\n IntegrationSecrets,\n IntegrationFetch,\n IntegrationLogger,\n StorageEntry,\n StorageWriteOptions,\n StorageSchemas,\n StorageSchemaMap,\n StorageScopeSchemas,\n StorageKeyDeclaration,\n StorageKeyDeclarationInput,\n StorageKeySchema,\n} from './capability-context';\n\nexport type { ComponentReference } from './component-reference';\nexport { isComponentReference } from './component-reference';\n\n// Deterministic slug → uuid derivation. One slug, one product id, everywhere\n// and forever — the SDK, the seed generator and any future gate all agree by\n// computing rather than choosing.\nexport {\n DNS_NAMESPACE,\n EKANOS_INTEGRATION_NAMESPACE,\n derivePlanId,\n deriveProductId,\n uuidv5,\n} from './product-id';\n\n// Workspace-target contract (F10): one schema, host injects icon validity\nexport {\n WorkspaceTargetSchema,\n WorkspaceTargetListSchema,\n WorkspaceTargetSlugSchema,\n WorkspaceTargetLayoutSchema,\n createWorkspaceTargetSchema,\n createWorkspaceTargetListSchema,\n normalizeWorkspaceTargets,\n type WorkspaceTargetSchemaOptions,\n type WorkspaceTargetDefinition,\n type ResolvedWorkspaceTarget,\n} from './workspace-target';\n\n// The integration-definition contract\nexport {\n IntegrationDefinitionSchema,\n parseIntegrationDefinition,\n validateIntegrationDefinitions,\n // Non-throwing sibling collectors (0.2.0 surface add): recover structured\n // findings instead of a thrown, pre-formatted string. `@ekanos/cli validate`\n // consumes these; the throwing functions above are built on the same rules.\n collectDefinitionFindings,\n collectCollisionFindings,\n getDiscoveredToolName,\n assertPlainDeclaration,\n deepFreezeDefinition,\n // The storage data_type allowlists. Exported because the HOST imports them\n // for its runtime `ctx.storage` check — one list, validated at authoring\n // time here and at access time there, so the two cannot drift.\n ACCOUNT_STORAGE_DATA_TYPES,\n USER_STORAGE_DATA_TYPES,\n RESERVED_STORAGE_DATA_TYPES,\n} from './integration-definition';\n\nexport type {\n Finding,\n CollectDefinitionOptions,\n} from './integration-definition';\n\nexport type {\n IntegrationDefinition,\n IntegrationComponentDeclarations,\n IntegrationProposals,\n IntegrationCapabilityDeclaration,\n IntegrationPermissionDeclaration,\n PartnerWidgetDeclaration,\n PartnerToolModule,\n PartnerToolParameters,\n ToolClassificationProposal,\n DefinitionCollisionInput,\n FirstPartyInventory,\n JsonValue,\n // Event surfaces (webhooks, schedules, OAuth) — interface-first: the local\n // harness executes them today; the host transports bind to them later.\n PartnerWebhookDeclaration,\n WebhookSignatureDeclaration,\n WebhookEvent,\n WebhookResult,\n PartnerScheduleDeclaration,\n ScheduleInvocation,\n ScheduleResult,\n PartnerOAuthDeclaration,\n OAuthProviderDeclaration,\n OAuthTokens,\n // Activation lifecycle hook — cache seeding / eager validation at connect\n // time, non-fatal in v1. See the TSDoc on OnActivateHandler.\n OnActivateHandler,\n} from './integration-definition';\n"]}
@@ -17,6 +17,37 @@ type JsonPrimitive = string | number | boolean | null;
17
17
  export type JsonValue = JsonPrimitive | JsonValue[] | {
18
18
  [key: string]: JsonValue;
19
19
  };
20
+ /**
21
+ * The `data_type` values each storage scope permits.
22
+ *
23
+ * THIS IS THE SOURCE OF TRUTH, and it lives here rather than in the host
24
+ * because this package is the one a partner's `defineIntegration()` validates
25
+ * against — a declaration naming an impossible data_type should fail at
26
+ * authoring time, not on the first `ctx.storage` call in production. The host
27
+ * (`apps/web/lib/server/integration-context.ts`) imports these same arrays for
28
+ * its runtime check, so the two cannot drift.
29
+ *
30
+ * They mirror the DB CHECK constraints on `account_product_data` and
31
+ * `user_product_data` (`apps/web/supabase/schemas/26-integrations.sql`), with
32
+ * one deliberate subtraction: see `RESERVED_STORAGE_DATA_TYPES`.
33
+ */
34
+ export declare const ACCOUNT_STORAGE_DATA_TYPES: readonly ["activation", "settings", "metrics_summary", "sync_state", "cache"];
35
+ export declare const USER_STORAGE_DATA_TYPES: readonly ["config", "preferences", "cache"];
36
+ /**
37
+ * In the DB CHECK but NEVER addressable through `ctx.storage`.
38
+ *
39
+ * `secret` rows are host-managed per-name credential rows whose values live in
40
+ * Vault; partner code reaches them only through `ctx.secrets`, which never
41
+ * exposes a value or a vault id. Letting a partner DECLARE `secret` storage
42
+ * would hand them a key that collides with host-managed secret rows, so it is
43
+ * rejected here with its own message rather than the generic "not an allowed
44
+ * data_type" — an author who wrote `secret` meant something specific and needs
45
+ * to be pointed at `ctx.secrets`.
46
+ *
47
+ * Kept as its own list rather than simply omitted from the arrays above so the
48
+ * reason survives: `secret` is a real column value, not a typo.
49
+ */
50
+ export declare const RESERVED_STORAGE_DATA_TYPES: readonly ["secret"];
20
51
  declare const capabilitySchema: z.ZodObject<{
21
52
  label: z.ZodString;
22
53
  description: z.ZodString;
@@ -854,8 +885,8 @@ export declare const IntegrationDefinitionSchema: z.ZodEffects<z.ZodObject<{
854
885
  outputExample?: Record<string, JsonValue> | undefined;
855
886
  }>, "many">>;
856
887
  storage: z.ZodOptional<z.ZodObject<{
857
- account: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodType<StorageKeyDeclarationInput, z.ZodTypeDef, StorageKeyDeclarationInput>, StorageKeyDeclarationInput, StorageKeyDeclarationInput>>>;
858
- user: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodType<StorageKeyDeclarationInput, z.ZodTypeDef, StorageKeyDeclarationInput>, StorageKeyDeclarationInput, StorageKeyDeclarationInput>>>;
888
+ account: z.ZodOptional<z.ZodRecord<z.ZodEffects<z.ZodString, string, string>, z.ZodEffects<z.ZodType<StorageKeyDeclarationInput, z.ZodTypeDef, StorageKeyDeclarationInput>, StorageKeyDeclarationInput, StorageKeyDeclarationInput>>>;
889
+ user: z.ZodOptional<z.ZodRecord<z.ZodEffects<z.ZodString, string, string>, z.ZodEffects<z.ZodType<StorageKeyDeclarationInput, z.ZodTypeDef, StorageKeyDeclarationInput>, StorageKeyDeclarationInput, StorageKeyDeclarationInput>>>;
859
890
  }, "strict", z.ZodTypeAny, {
860
891
  user?: Record<string, StorageKeyDeclarationInput> | undefined;
861
892
  account?: Record<string, StorageKeyDeclarationInput> | undefined;
@@ -970,6 +1001,7 @@ export declare const IntegrationDefinitionSchema: z.ZodEffects<z.ZodObject<{
970
1001
  };
971
1002
  onTokens: (ctx: never, arg: never) => Promise<unknown>;
972
1003
  }>>;
1004
+ onActivate: z.ZodOptional<z.ZodType<(ctx: never, arg: never) => Promise<unknown>, z.ZodTypeDef, (ctx: never, arg: never) => Promise<unknown>>>;
973
1005
  proposes: z.ZodOptional<z.ZodObject<{
974
1006
  credentialModel: z.ZodOptional<z.ZodEnum<["account", "user", "source"]>>;
975
1007
  tools: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -1000,6 +1032,7 @@ export declare const IntegrationDefinitionSchema: z.ZodEffects<z.ZodObject<{
1000
1032
  slug: string;
1001
1033
  name: string;
1002
1034
  version: string;
1035
+ onActivate?: ((ctx: never, arg: never) => Promise<unknown>) | undefined;
1003
1036
  tools?: {
1004
1037
  description: string;
1005
1038
  name: string;
@@ -1131,6 +1164,7 @@ export declare const IntegrationDefinitionSchema: z.ZodEffects<z.ZodObject<{
1131
1164
  slug: string;
1132
1165
  name: string;
1133
1166
  version: string;
1167
+ onActivate?: ((ctx: never, arg: never) => Promise<unknown>) | undefined;
1134
1168
  tools?: {
1135
1169
  description: string;
1136
1170
  name: string;
@@ -1262,6 +1296,7 @@ export declare const IntegrationDefinitionSchema: z.ZodEffects<z.ZodObject<{
1262
1296
  slug: string;
1263
1297
  name: string;
1264
1298
  version: string;
1299
+ onActivate?: ((ctx: never, arg: never) => Promise<unknown>) | undefined;
1265
1300
  tools?: {
1266
1301
  description: string;
1267
1302
  name: string;
@@ -1393,6 +1428,7 @@ export declare const IntegrationDefinitionSchema: z.ZodEffects<z.ZodObject<{
1393
1428
  slug: string;
1394
1429
  name: string;
1395
1430
  version: string;
1431
+ onActivate?: ((ctx: never, arg: never) => Promise<unknown>) | undefined;
1396
1432
  tools?: {
1397
1433
  description: string;
1398
1434
  name: string;
@@ -1664,6 +1700,32 @@ export interface PartnerOAuthDeclaration<Schemas extends StorageSchemas = Storag
1664
1700
  };
1665
1701
  onTokens: (ctx: IntegrationContext<Schemas>, tokens: OAuthTokens) => Promise<void>;
1666
1702
  }
1703
+ /**
1704
+ * The v1 activation lifecycle hook. Runs server-side with the full capability
1705
+ * ctx (egress/storage/secrets enforcement identical to `schedules[].handler`)
1706
+ * at two points:
1707
+ *
1708
+ * (a) once, after an activation is first PERSISTED — the moment a
1709
+ * cache-backed dashboard would otherwise stay empty until the next
1710
+ * schedule tick (up to a full interval);
1711
+ * (b) again, every time activationData is UPDATED — so a changed credential
1712
+ * or setting doesn't leave a stale cache in place until the next tick.
1713
+ *
1714
+ * **v1 errors are NON-FATAL.** A throw is logged and surfaced to the user as
1715
+ * a warning; the activation stays active. This hook exists for CACHE SEEDING
1716
+ * and EAGER VALIDATION (warm the `clientReadable` storage a widget reads,
1717
+ * sanity-check a credential up front) — it is explicitly NOT a connect gate.
1718
+ * A handler that must be able to REJECT the connection (fail-the-connect
1719
+ * credential validation) needs a different, future field with fatal
1720
+ * semantics; `onActivate` is not it, and must not be repurposed as one.
1721
+ *
1722
+ * Pairs with a fingerprint-guarded cache (see the SDK README's "Activation"
1723
+ * section): a cache invalidated only by age still serves the PREVIOUS
1724
+ * activation's data for a window even with this hook wired up — the
1725
+ * fingerprint pattern is what closes that window, `onActivate` only
1726
+ * shortens it.
1727
+ */
1728
+ export type OnActivateHandler<Schemas extends StorageSchemas = StorageSchemas> = (ctx: IntegrationContext<Schemas>) => Promise<void>;
1667
1729
  export interface IntegrationDefinition<Schemas extends StorageSchemas = StorageSchemas> {
1668
1730
  slug: string;
1669
1731
  name: string;
@@ -1678,6 +1740,7 @@ export interface IntegrationDefinition<Schemas extends StorageSchemas = StorageS
1678
1740
  egress?: string[];
1679
1741
  webhooks?: PartnerWebhookDeclaration<Schemas>[];
1680
1742
  schedules?: PartnerScheduleDeclaration<Schemas>[];
1743
+ onActivate?: OnActivateHandler<Schemas>;
1681
1744
  oauth?: PartnerOAuthDeclaration<Schemas>;
1682
1745
  proposes?: IntegrationProposals;
1683
1746
  }
@@ -1753,8 +1816,8 @@ export declare function parseIntegrationDefinition(input: unknown): IntegrationD
1753
1816
  * to the same EFFECTIVE name (`{slug:"foo",tool:"bar_baz"}` and
1754
1817
  * `{slug:"foo-bar",tool:"baz"}` both become `foo_bar_baz`), so collision
1755
1818
  * checking MUST compare effective names, and runtime discovery MUST throw on a
1756
- * duplicate assignment. Both call this one helper
1757
- * (`packages/agents/src/tools/tool-discovery.ts`).
1819
+ * duplicate assignment. Host-side tool discovery calls this same helper, so
1820
+ * there is one definition of the effective name.
1758
1821
  */
1759
1822
  export declare function getDiscoveredToolName(slug: string, rawName: string): string;
1760
1823
  /**
@@ -30,7 +30,7 @@ function componentRefSchema(what) {
30
30
  });
31
31
  }
32
32
  const zodSchemaRef = z.custom((value) => typeof (value === null || value === void 0 ? void 0 : value.safeParse) === 'function', {
33
- message: 'Every storage key must declare a zod schema (e.g. z.object({ … })) — ctx.storage validates reads and writes against it (capability-context ruling 1).',
33
+ message: 'Every storage key must declare a zod schema (e.g. z.object({ … })) — ctx.storage validates reads and writes against it.',
34
34
  });
35
35
  const nonEmpty = (what) => z.string().min(1, { message: `${what} must be a non-empty string.` });
36
36
  const slugSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {
@@ -39,9 +39,90 @@ const slugSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {
39
39
  const widgetIdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {
40
40
  message: 'Widget ids are kebab-case and globally unique, e.g. "acme-crm-pipeline" — prefix with the integration slug to stay collision-free.',
41
41
  });
42
- const storageKeySchema = z.string().regex(/^[a-z0-9_-]+(?:\/[a-z0-9_-]+)?$/, {
43
- message: 'Storage keys are "<dataType>" or "<dataType>/<subtype>" in lowercase [a-z0-9_-] — they map onto the account/user product-data columns (capability-context ruling 6).',
44
- });
42
+ /**
43
+ * The `data_type` values each storage scope permits.
44
+ *
45
+ * THIS IS THE SOURCE OF TRUTH, and it lives here rather than in the host
46
+ * because this package is the one a partner's `defineIntegration()` validates
47
+ * against — a declaration naming an impossible data_type should fail at
48
+ * authoring time, not on the first `ctx.storage` call in production. The host
49
+ * (`apps/web/lib/server/integration-context.ts`) imports these same arrays for
50
+ * its runtime check, so the two cannot drift.
51
+ *
52
+ * They mirror the DB CHECK constraints on `account_product_data` and
53
+ * `user_product_data` (`apps/web/supabase/schemas/26-integrations.sql`), with
54
+ * one deliberate subtraction: see `RESERVED_STORAGE_DATA_TYPES`.
55
+ */
56
+ export const ACCOUNT_STORAGE_DATA_TYPES = [
57
+ 'activation',
58
+ 'settings',
59
+ 'metrics_summary',
60
+ 'sync_state',
61
+ 'cache',
62
+ ];
63
+ export const USER_STORAGE_DATA_TYPES = [
64
+ 'config',
65
+ 'preferences',
66
+ 'cache',
67
+ ];
68
+ /**
69
+ * In the DB CHECK but NEVER addressable through `ctx.storage`.
70
+ *
71
+ * `secret` rows are host-managed per-name credential rows whose values live in
72
+ * Vault; partner code reaches them only through `ctx.secrets`, which never
73
+ * exposes a value or a vault id. Letting a partner DECLARE `secret` storage
74
+ * would hand them a key that collides with host-managed secret rows, so it is
75
+ * rejected here with its own message rather than the generic "not an allowed
76
+ * data_type" — an author who wrote `secret` meant something specific and needs
77
+ * to be pointed at `ctx.secrets`.
78
+ *
79
+ * Kept as its own list rather than simply omitted from the arrays above so the
80
+ * reason survives: `secret` is a real column value, not a typo.
81
+ */
82
+ export const RESERVED_STORAGE_DATA_TYPES = ['secret'];
83
+ /** VARCHAR(100) on both the data_type and data_subtype columns. */
84
+ const MAX_STORAGE_SEGMENT_LENGTH = 100;
85
+ const STORAGE_KEY_SHAPE = /^[a-z0-9_-]+(?:\/[a-z0-9_-]+)?$/;
86
+ /**
87
+ * A storage key is `"<dataType>"` or `"<dataType>/<subtype>"`, mapped onto the
88
+ * `(data_type, data_subtype)` columns. Scope-specific because the two tables
89
+ * carry different CHECK constraints — `settings` is an account data_type and
90
+ * `preferences` a user one, and neither is valid in the other's scope.
91
+ */
92
+ function storageKeySchemaFor(scope) {
93
+ const allowed = scope === 'account' ? ACCOUNT_STORAGE_DATA_TYPES : USER_STORAGE_DATA_TYPES;
94
+ return z.string().superRefine((key, ctx) => {
95
+ const fail = (message) => ctx.addIssue({ code: z.ZodIssueCode.custom, message });
96
+ if (!STORAGE_KEY_SHAPE.test(key)) {
97
+ fail(`Storage key "${key}" is malformed. Keys are "<dataType>" or ` +
98
+ `"<dataType>/<subtype>" in lowercase [a-z0-9_-], with at most one ` +
99
+ `"/" and no empty segment.`);
100
+ return;
101
+ }
102
+ const slashIndex = key.indexOf('/');
103
+ const dataType = slashIndex === -1 ? key : key.slice(0, slashIndex);
104
+ const dataSubtype = slashIndex === -1 ? undefined : key.slice(slashIndex + 1);
105
+ if (RESERVED_STORAGE_DATA_TYPES.includes(dataType)) {
106
+ fail(`Storage key "${key}" uses the reserved "${dataType}" data_type, ` +
107
+ `which ctx.storage can never read or write. Integration secrets are ` +
108
+ `host-managed — declare nothing here and use ctx.secrets.get/set/names ` +
109
+ `instead; a secret's value and its vault id are never reachable ` +
110
+ `through ctx.storage.`);
111
+ return;
112
+ }
113
+ if (!allowed.includes(dataType)) {
114
+ fail(`Storage key "${key}" is invalid for the ${scope} scope: ` +
115
+ `"${dataType}" is not an allowed ${scope} data_type. Use one of ` +
116
+ `[${allowed.join(', ')}] (the database CHECK constraint).`);
117
+ return;
118
+ }
119
+ if (dataSubtype !== undefined &&
120
+ dataSubtype.length > MAX_STORAGE_SEGMENT_LENGTH) {
121
+ fail(`Storage key "${key}" has a subtype longer than ` +
122
+ `${MAX_STORAGE_SEGMENT_LENGTH} characters (the column width).`);
123
+ }
124
+ });
125
+ }
45
126
  const toolNameSchema = z.string().regex(/^[a-z][a-z0-9_]*$/, {
46
127
  message: 'Tool names are lowercase snake_case starting with a letter, e.g. "list_invoices" — the model calls them by this exact string.',
47
128
  });
@@ -263,6 +344,13 @@ const oauthSchema = z
263
344
  onTokens: handlerSchema('oauth.onTokens', '(ctx, tokens) => Promise<void>'),
264
345
  })
265
346
  .strict();
347
+ // ---- Activation lifecycle hook ---------------------------------------------
348
+ //
349
+ // Declared and checked exactly like the other handler-bearing fields above:
350
+ // structural function check via `handlerSchema`, carried through the parse
351
+ // untouched. See the `onActivate` TSDoc on `IntegrationDefinition` for the
352
+ // full semantics (when it runs, why v1 errors are non-fatal).
353
+ const onActivateSchema = handlerSchema('onActivate', '(ctx) => Promise<void>');
266
354
  const toolClassificationProposalSchema = z
267
355
  .object({
268
356
  effect: z.enum(['read', 'write']).optional(),
@@ -312,7 +400,7 @@ const storageKeyDeclarationRef = z
312
400
  !isZodSchemaLike(value.schema)) {
313
401
  ctx.addIssue({
314
402
  code: z.ZodIssueCode.custom,
315
- message: 'Every storage key must declare either a zod schema (e.g. z.object({ … })) or a { schema, clientReadable } descriptor — ctx.storage validates reads and writes against the schema (capability-context ruling 1), and clientReadable (default false) is what opts the key in to the browser-readable storage route.',
403
+ message: 'Every storage key must declare either a zod schema (e.g. z.object({ … })) or a { schema, clientReadable } descriptor — ctx.storage validates reads and writes against the schema, and clientReadable (default false) is what opts the key in to the browser-readable storage route.',
316
404
  });
317
405
  return;
318
406
  }
@@ -330,7 +418,7 @@ const storageKeyDeclarationRef = z
330
418
  });
331
419
  }
332
420
  });
333
- const storageScopeSchema = z.record(storageKeySchema, storageKeyDeclarationRef);
421
+ const storageScopeSchemaFor = (scope) => z.record(storageKeySchemaFor(scope), storageKeyDeclarationRef);
334
422
  const componentsSchema = z
335
423
  .object({
336
424
  activationForm: componentRefSchema('components.activationForm').optional(),
@@ -361,8 +449,8 @@ export const IntegrationDefinitionSchema = z
361
449
  tools: z.array(toolSchema).optional(),
362
450
  storage: z
363
451
  .object({
364
- account: storageScopeSchema.optional(),
365
- user: storageScopeSchema.optional(),
452
+ account: storageScopeSchemaFor('account').optional(),
453
+ user: storageScopeSchemaFor('user').optional(),
366
454
  })
367
455
  .strict()
368
456
  .optional(),
@@ -370,6 +458,7 @@ export const IntegrationDefinitionSchema = z
370
458
  webhooks: z.array(webhookSchema).optional(),
371
459
  schedules: z.array(scheduleSchema).optional(),
372
460
  oauth: oauthSchema.optional(),
461
+ onActivate: onActivateSchema.optional(),
373
462
  proposes: proposalsSchema.optional(),
374
463
  })
375
464
  .strict()
@@ -656,8 +745,8 @@ export function parseIntegrationDefinition(input) {
656
745
  * to the same EFFECTIVE name (`{slug:"foo",tool:"bar_baz"}` and
657
746
  * `{slug:"foo-bar",tool:"baz"}` both become `foo_bar_baz`), so collision
658
747
  * checking MUST compare effective names, and runtime discovery MUST throw on a
659
- * duplicate assignment. Both call this one helper
660
- * (`packages/agents/src/tools/tool-discovery.ts`).
748
+ * duplicate assignment. Host-side tool discovery calls this same helper, so
749
+ * there is one definition of the effective name.
661
750
  */
662
751
  export function getDiscoveredToolName(slug, rawName) {
663
752
  const slugPrefix = slug.replace(/-/g, '_');
@@ -1 +1 @@
1
- {"version":3,"file":"integration-definition.js","sourceRoot":"","sources":["../src/integration-definition.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The canonical integration-definition contract: ONE zod schema and ONE set\n * of inferred/hand-written types (F9). `@ekanos/sdk`'s `defineIntegration()`\n * validates against this schema at authoring time; `@kit/integrations-core`'s\n * `registerPartnerIntegration()` re-validates against the SAME schema at the\n * host trust boundary (F3). Both packages depend on this one; it depends on\n * neither, so there is no cycle and no hand-written structural twin.\n *\n * Dependency-pure (zod only): partner component refs are checked\n * structurally via `ComponentReference`, so no React dependency leaks in.\n */\nimport { z } from 'zod';\n\nimport type {\n IntegrationContext,\n StorageKeyDeclarationInput,\n StorageSchemas,\n} from './capability-context';\nimport type { ComponentReference } from './component-reference';\nimport { isComponentReference } from './component-reference';\nimport {\n type WorkspaceTargetDefinition,\n WorkspaceTargetListSchema,\n} from './workspace-target';\n\n// ---- JSON-compatible values (F4) ------------------------------------------\n\ntype JsonPrimitive = string | number | boolean | null;\nexport type JsonValue =\n | JsonPrimitive\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nconst jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>\n z.union([\n z.string(),\n // `.finite()` rejects Infinity/-Infinity AND NaN (Number.isFinite) — all\n // of which JSON.stringify silently turns to `null`, so they are not\n // JSON-compatible values (F4).\n z.number().finite(),\n z.boolean(),\n z.null(),\n z.array(jsonValueSchema),\n z.record(jsonValueSchema),\n ]),\n);\n\n// ---- Leaf validators -------------------------------------------------------\n\nfunction componentRefSchema(what: string) {\n return z.custom<ComponentReference>(isComponentReference, {\n message: `${what} must be a React component reference (a function component, or a memo/forwardRef/lazy wrapper) — pass the component itself, not an element or a module path.`,\n });\n}\n\nconst zodSchemaRef = z.custom<z.ZodType>(\n (value) =>\n typeof (value as { safeParse?: unknown } | null)?.safeParse === 'function',\n {\n message:\n 'Every storage key must declare a zod schema (e.g. z.object({ … })) — ctx.storage validates reads and writes against it (capability-context ruling 1).',\n },\n);\n\nconst nonEmpty = (what: string) =>\n z.string().min(1, { message: `${what} must be a non-empty string.` });\n\nconst slugSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {\n message:\n 'slug must be kebab-case ([a-z0-9] segments separated by single hyphens), e.g. \"acme-crm\" — it becomes the product slug, route segment, and MCP namespace.',\n});\n\nconst widgetIdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {\n message:\n 'Widget ids are kebab-case and globally unique, e.g. \"acme-crm-pipeline\" — prefix with the integration slug to stay collision-free.',\n});\n\nconst storageKeySchema = z.string().regex(/^[a-z0-9_-]+(?:\\/[a-z0-9_-]+)?$/, {\n message:\n 'Storage keys are \"<dataType>\" or \"<dataType>/<subtype>\" in lowercase [a-z0-9_-] — they map onto the account/user product-data columns (capability-context ruling 6).',\n});\n\nconst toolNameSchema = z.string().regex(/^[a-z][a-z0-9_]*$/, {\n message:\n 'Tool names are lowercase snake_case starting with a letter, e.g. \"list_invoices\" — the model calls them by this exact string.',\n});\n\nconst webhookIdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {\n message:\n 'Webhook ids are kebab-case, e.g. \"payment-updated\" — the host ingress route addresses the handler by this exact string.',\n});\n\nconst scheduleIdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {\n message:\n 'Schedule ids are kebab-case, e.g. \"daily-reconcile\" — the host scheduler addresses the handler by this exact string.',\n});\n\n// Origin-only egress entries (ruling 3): absolute https origins, optional\n// `*.` subdomain wildcard, no path/query/hash/credentials/http. Kept as a\n// pure regex here so this package depends on nothing; the SDK's shared\n// `parseEgressEntry`/`isEgressAllowed` matcher enforces the identical shape\n// at runtime.\nconst egressEntrySchema = z.string().superRefine((entry, ctx) => {\n const match = /^https:\\/\\/(\\*\\.)?([a-z0-9.-]+)(?::(\\d+))?$/i.exec(entry);\n if (!match) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `Egress entry \"${entry}\" is invalid. Entries are https origins only — ` +\n `\"https://api.example.com\" (exact) or \"https://*.example.com\" ` +\n `(subdomain wildcard): scheme + host + optional port, no path, query, ` +\n `hash, credentials, or http. Fix it in the integration's egress list.`,\n });\n return;\n }\n const host = match[2] ?? '';\n if (host.includes('*') || host.startsWith('.') || host.endsWith('.')) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Egress entry \"${entry}\" has a malformed host — wildcards are only supported as a single leading \"*.\" (e.g. \"https://*.example.com\").`,\n });\n }\n});\n\n// ---- Sub-object schemas ----------------------------------------------------\n\nconst capabilitySchema = z\n .object({\n label: nonEmpty('capabilities[].label'),\n description: nonEmpty('capabilities[].description'),\n icon: componentRefSchema('capabilities[].icon').optional(),\n })\n .strict();\n\nconst permissionSchema = z\n .object({\n label: nonEmpty('permissions[].label'),\n detail: nonEmpty('permissions[].detail'),\n type: z.enum(['read', 'write']),\n })\n .strict();\n\nconst gridUnitSchema = z\n .object({\n cols: z.number().int().positive(),\n rows: z.number().int().positive(),\n })\n .strict();\n\nconst layoutBoxSchema = z\n .object({\n x: z.number(),\n y: z.number(),\n w: z.number(),\n h: z.number(),\n maxHeight: z.number().optional(),\n })\n .strict();\n\n/**\n * F7: authorable widget fields ONLY. Host-resolved fields (`productId`,\n * `widgetConfigId`, `workspaceId`, `collapsed`, `isPinned`, `health`,\n * `integrationMetadata`) are absent by design and rejected at runtime by\n * `.strict()` — the type and the runtime agree. The host adapter maps this\n * into the full `WidgetConfig`.\n */\nconst widgetSchema = z\n .object({\n id: widgetIdSchema,\n name: nonEmpty('widgets[].name'),\n component: componentRefSchema('widgets[].component'),\n widgetState: z.enum(['active', 'inactive', 'disabled']),\n gridSize: z.union([gridUnitSchema, z.array(gridUnitSchema)]).optional(),\n gridPosition: z\n .object({ col: z.number(), row: z.number() })\n .strict()\n .optional(),\n layouts: z\n .object({\n lg: layoutBoxSchema.optional(),\n md: layoutBoxSchema.optional(),\n sm: layoutBoxSchema.optional(),\n })\n .strict()\n .optional(),\n category: z\n .object({\n id: z.string(),\n name: z.string(),\n slug: z.string(),\n icon: z.string().nullable(),\n })\n .strict()\n .optional(),\n isCollapsible: z.boolean().optional(),\n isPinnable: z.boolean().optional(),\n aiFooterEnabled: z.boolean().optional(),\n })\n .strict();\n\nconst toolParametersSchema = z\n .object({\n type: z.literal('object'),\n properties: z.record(jsonValueSchema).optional(),\n required: z.array(z.string()).optional(),\n additionalProperties: z.boolean().optional(),\n })\n .strict();\n\nconst toolRunSchema = z.custom<\n (ctx: never, args: Record<string, unknown>) => Promise<unknown>\n>((value) => typeof value === 'function', {\n message:\n 'tools[].run must be a function (ctx, args) => Promise<result> — it receives the host-scoped IntegrationContext, never a raw client.',\n});\n\nconst toolSchema = z\n .object({\n name: toolNameSchema,\n description: nonEmpty('tools[].description'),\n parameters: toolParametersSchema.optional(),\n run: toolRunSchema,\n outputExample: z.record(jsonValueSchema).optional(),\n })\n .strict();\n\n// ---- Event surfaces (webhooks, schedules, OAuth) ---------------------------\n//\n// Declared exactly like MCP tools: metadata parsed strictly, handlers checked\n// structurally as functions (`z.custom`) and carried through the parse\n// untouched. The declarations are the CONTRACT; every transport — the local\n// harness today, the host's public ingress/scheduler/hosted-callback later —\n// binds to these same fields, so a partner package never changes when the\n// real transports arrive.\n\nfunction handlerSchema(what: string, shape: string) {\n return z.custom<(ctx: never, arg: never) => Promise<unknown>>(\n (value) => typeof value === 'function',\n {\n message: `${what} must be a function ${shape} — it receives the host-scoped IntegrationContext, never a raw request or client.`,\n },\n );\n}\n\nconst payloadSchemaRef = z.custom<z.ZodType>(\n (value) =>\n typeof (value as { safeParse?: unknown } | null)?.safeParse === 'function',\n {\n message:\n 'webhooks[].payloadSchema must be a zod schema (e.g. z.object({ … })) — the transport validates every delivery against it before the handler runs.',\n },\n);\n\n/**\n * How the TRANSPORT verifies a delivery. Verification is never the partner's\n * job: the declaration names the signature header and the secret that signs\n * it; the host ingress enforces it (the local harness logs it as skipped).\n * `'none'` is an explicit statement that the source is unsigned.\n */\nconst webhookSignatureSchema = z.union([\n z.literal('none'),\n z\n .object({\n header: nonEmpty('webhooks[].signature.header'),\n secretName: nonEmpty('webhooks[].signature.secretName'),\n })\n .strict(),\n]);\n\nconst webhookSchema = z\n .object({\n id: webhookIdSchema,\n description: nonEmpty('webhooks[].description'),\n payloadSchema: payloadSchemaRef,\n signature: webhookSignatureSchema,\n examplePayload: z.record(jsonValueSchema).optional(),\n handler: handlerSchema(\n 'webhooks[].handler',\n '(ctx, event) => Promise<WebhookResult>',\n ),\n })\n .strict();\n\nconst scheduleSchema = z\n .object({\n id: scheduleIdSchema,\n description: nonEmpty('schedules[].description'),\n // Presence only here — the dependency-pure schema package stays zod-only,\n // so the real 5-field cron syntax check lives in the SDK layer\n // (`defineIntegration()`), the same way it layers cross-field rules today.\n cron: nonEmpty('schedules[].cron'),\n handler: handlerSchema(\n 'schedules[].handler',\n '(ctx, invocation) => Promise<ScheduleResult>',\n ),\n })\n .strict();\n\nconst httpsUrlSchema = (what: string) =>\n z.string().superRefine((value, ctx) => {\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${what} must be an absolute URL, e.g. \"https://provider.example/oauth/authorize\".`,\n });\n return;\n }\n if (parsed.protocol !== 'https:') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${what} must use https — OAuth endpoints are never plain http.`,\n });\n }\n if (parsed.username !== '' || parsed.password !== '') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${what} must not embed credentials.`,\n });\n }\n });\n\nconst oauthSchema = z\n .object({\n provider: z\n .object({\n authorizationUrl: httpsUrlSchema('oauth.provider.authorizationUrl'),\n tokenUrl: httpsUrlSchema('oauth.provider.tokenUrl'),\n scopes: z.array(nonEmpty('oauth.provider.scopes[]')),\n pkce: z.boolean().optional(),\n })\n .strict(),\n credentials: z\n .object({\n clientIdSecretName: nonEmpty('oauth.credentials.clientIdSecretName'),\n clientSecretSecretName: nonEmpty(\n 'oauth.credentials.clientSecretSecretName',\n ),\n })\n .strict(),\n onTokens: handlerSchema('oauth.onTokens', '(ctx, tokens) => Promise<void>'),\n })\n .strict();\n\nconst toolClassificationProposalSchema = z\n .object({\n effect: z.enum(['read', 'write']).optional(),\n sensitivity: z.enum(['public', 'internal', 'pii', 'financial']).optional(),\n })\n .strict();\n\nconst proposalsSchema = z\n .object({\n credentialModel: z.enum(['account', 'user', 'source']).optional(),\n tools: z.record(toolClassificationProposalSchema).optional(),\n })\n .strict();\n\n/**\n * A storage key's EXPLICIT declaration: the zod schema plus its exposure\n * flags. `clientReadable` is the only way a declared key becomes readable by\n * the browser through the generic storage route — and it defaults to false,\n * so the bare-schema form stays server-only exactly as it always was.\n * `.strict()` keeps an unrecognized flag (a typo like `clientReadible`) an\n * error rather than a silently-ignored key whose author believes it is\n * exposed — or, worse, believes it is not.\n */\nconst storageKeyDeclarationSchema = z\n .object({\n schema: zodSchemaRef,\n clientReadable: z.boolean().optional(),\n })\n .strict();\n\n/** Duck-typed so a partner's own bundled zod copy still reads as a schema. */\nfunction isZodSchemaLike(value: unknown): boolean {\n return (\n typeof (value as { safeParse?: unknown } | null)?.safeParse === 'function'\n );\n}\n\n/**\n * Either declaration form, hand-routed rather than expressed as `z.union` so\n * the failure message stays specific. A union reports a bare \"Invalid input\"\n * for every wrong shape, which would lose both the \"declare a zod schema\"\n * guidance AND the strict-descriptor typo report — the two errors an author\n * is actually going to hit.\n */\nconst storageKeyDeclarationRef = z\n .custom<StorageKeyDeclarationInput>(() => true)\n .superRefine((value, ctx) => {\n if (isZodSchemaLike(value)) return;\n\n const looksLikeDescriptor =\n value !== null && typeof value === 'object' && !Array.isArray(value);\n\n // Neither form: name both, since the descriptor is the less obvious one.\n if (\n !looksLikeDescriptor ||\n !isZodSchemaLike((value as { schema?: unknown }).schema)\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'Every storage key must declare either a zod schema (e.g. z.object({ … })) or a { schema, clientReadable } descriptor — ctx.storage validates reads and writes against the schema (capability-context ruling 1), and clientReadable (default false) is what opts the key in to the browser-readable storage route.',\n });\n return;\n }\n\n // A real descriptor with a real schema — report its own issues verbatim\n // (an unrecognized flag, a non-boolean clientReadable) rather than the\n // generic message, which would send the author looking in the wrong place.\n const result = storageKeyDeclarationSchema.safeParse(value);\n if (result.success) return;\n\n for (const issue of result.error.issues) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: issue.path,\n message: issue.message,\n });\n }\n });\n\nconst storageScopeSchema = z.record(storageKeySchema, storageKeyDeclarationRef);\n\nconst componentsSchema = z\n .object({\n activationForm: componentRefSchema('components.activationForm').optional(),\n marketplaceTile: componentRefSchema(\n 'components.marketplaceTile',\n ).optional(),\n widgets: z.array(widgetSchema).optional(),\n })\n .strict();\n\n/**\n * THE canonical schema. Strict everywhere: an unrecognized key is an error,\n * which is what keeps host-assigned fields (productId, kind, trust tier,\n * credentialModel, per-tool effect/sensitivity, host-resolved widget fields)\n * structurally un-settable at runtime, not merely absent from the type.\n */\nexport const IntegrationDefinitionSchema = z\n .object({\n slug: slugSchema,\n name: nonEmpty('name'),\n description: nonEmpty('description'),\n version: z\n .string()\n .regex(\n /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/,\n {\n message:\n 'version must be semver (\"1.0.0\", optionally with a prerelease/build suffix) — promotion diffs definitions by it.',\n },\n ),\n capabilities: z.array(capabilitySchema).optional(),\n permissions: z.array(permissionSchema).optional(),\n components: componentsSchema.optional(),\n workspaceTargets: WorkspaceTargetListSchema.optional(),\n tools: z.array(toolSchema).optional(),\n storage: z\n .object({\n account: storageScopeSchema.optional(),\n user: storageScopeSchema.optional(),\n })\n .strict()\n .optional(),\n egress: z.array(egressEntrySchema).optional(),\n webhooks: z.array(webhookSchema).optional(),\n schedules: z.array(scheduleSchema).optional(),\n oauth: oauthSchema.optional(),\n proposes: proposalsSchema.optional(),\n })\n .strict()\n .superRefine((definition, ctx) => {\n const webhookIds = (definition.webhooks ?? []).map((w) => w.id);\n for (const id of findDuplicates(webhookIds)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['webhooks'],\n message: `Webhook id \"${id}\" is declared more than once — give every webhook a unique id.`,\n });\n }\n\n const scheduleIds = (definition.schedules ?? []).map((s) => s.id);\n for (const id of findDuplicates(scheduleIds)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['schedules'],\n message: `Schedule id \"${id}\" is declared more than once — give every schedule a unique id.`,\n });\n }\n\n const widgetIds = (definition.components?.widgets ?? []).map((w) => w.id);\n for (const id of findDuplicates(widgetIds)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['components', 'widgets'],\n message: `Widget id \"${id}\" is declared more than once — give every widget a unique id.`,\n });\n }\n\n const toolNames = (definition.tools ?? []).map((tool) => tool.name);\n for (const name of findDuplicates(toolNames)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['tools'],\n message: `Tool \"${name}\" is declared more than once — give every tool a unique name.`,\n });\n }\n\n const declaredTools = new Set(toolNames);\n for (const proposedName of Object.keys(definition.proposes?.tools ?? {})) {\n if (!declaredTools.has(proposedName)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['proposes', 'tools', proposedName],\n message: `proposes.tools[\"${proposedName}\"] does not match any declared tool — proposals are keyed by the exact tool name in \\`tools\\`.`,\n });\n }\n }\n });\n\n// ---- Hand-written generic types (the parts a non-generic schema cannot\n// express) plus inferred types for everything else. One home, imported by\n// both @ekanos/sdk and @kit/integrations-core. -------------------------------\n\nexport type ToolClassificationProposal = z.infer<\n typeof toolClassificationProposalSchema\n>;\nexport type IntegrationProposals = z.infer<typeof proposalsSchema>;\nexport type PartnerToolParameters = z.infer<typeof toolParametersSchema>;\nexport type IntegrationCapabilityDeclaration = z.infer<typeof capabilitySchema>;\nexport type IntegrationPermissionDeclaration = z.infer<typeof permissionSchema>;\nexport type PartnerWidgetDeclaration = z.infer<typeof widgetSchema>;\n\nexport interface PartnerToolModule<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n name: string;\n description: string;\n parameters?: PartnerToolParameters;\n /**\n * Contravariant function property (F8): the declared `Schemas` generic\n * types `ctx.storage` for the author. The generic cannot survive the\n * by-value, monomorphic host registration boundary — the adapter erases it\n * — but nothing rests on it surviving: the host constructs `ctx` FROM the\n * definition's own `storage` schemas, and `ctx.storage` validates every\n * read/write against them at runtime regardless of the handler's\n * annotation. The static generic is DX; the runtime schema is the guard.\n */\n run: (\n ctx: IntegrationContext<Schemas>,\n args: Record<string, unknown>,\n ) => Promise<unknown>;\n outputExample?: Record<string, JsonValue>;\n}\n\nexport interface IntegrationComponentDeclarations {\n activationForm?: ComponentReference;\n marketplaceTile?: ComponentReference;\n widgets?: PartnerWidgetDeclaration[];\n}\n\n// ---- Event-surface types (webhooks, schedules, OAuth) ----------------------\n//\n// Hand-written generics like `PartnerToolModule`: the `Schemas` generic types\n// `ctx.storage` for the author and is erased at the host boundary, where the\n// runtime storage validator — built from the definition's own `storage`\n// schemas — is the guard.\n\n/**\n * How the transport verifies a webhook delivery. `'none'` states explicitly\n * that the source is unsigned; otherwise the transport reads the named header\n * and verifies it against the named secret. Verification is the TRANSPORT's\n * job (the local harness logs it as skipped; the host ingress enforces it) —\n * never the partner handler's.\n */\nexport type WebhookSignatureDeclaration =\n | 'none'\n | { header: string; secretName: string };\n\n/**\n * One delivery, as the handler receives it: transport-assigned id and receipt\n * time, the delivery headers, and the payload ALREADY parsed and validated\n * against the declaration's `payloadSchema`. A payload that fails the schema\n * never reaches the handler.\n */\nexport interface WebhookEvent {\n id: string;\n /** ISO-8601 — when the transport accepted the delivery. */\n receivedAt: string;\n headers: Record<string, string>;\n /** The parsed, schema-validated payload (output of `payloadSchema`). */\n payload: unknown;\n}\n\n/**\n * What the handler tells the transport. `processed` acknowledges the event;\n * `ignored` acknowledges it as irrelevant (still a 2xx — the sender must not\n * retry). A handler that cannot process a valid event THROWS, which the\n * transport maps to a retryable failure.\n */\nexport interface WebhookResult {\n status: 'processed' | 'ignored';\n detail?: string;\n}\n\nexport interface PartnerWebhookDeclaration<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n id: string;\n description: string;\n /** Validates every delivery before the handler runs. */\n payloadSchema: z.ZodType;\n signature: WebhookSignatureDeclaration;\n /**\n * A representative payload, JSON-compatible. Seeds the harness's payload\n * editor and documents the shape beside the schema.\n */\n examplePayload?: Record<string, JsonValue>;\n handler: (\n ctx: IntegrationContext<Schemas>,\n event: WebhookEvent,\n ) => Promise<WebhookResult>;\n}\n\n/**\n * One firing, as the handler receives it. `trigger` distinguishes the real\n * scheduler from a human pressing \"Run now\" (harness or admin) — handlers may\n * branch on it (e.g. skip idempotency windows for manual runs) but must be\n * safe under both.\n */\nexport interface ScheduleInvocation {\n /** ISO-8601 — the tick this invocation stands for. */\n scheduledFor: string;\n /** ISO-8601 — when the handler actually started. */\n invokedAt: string;\n trigger: 'schedule' | 'manual';\n}\n\n/**\n * `completed` means the run did its work; `skipped` means it correctly did\n * nothing (not configured, nothing to do). A handler that fails THROWS, which\n * the transport records as a failed run.\n */\nexport interface ScheduleResult {\n status: 'completed' | 'skipped';\n detail?: string;\n}\n\nexport interface PartnerScheduleDeclaration<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n id: string;\n description: string;\n /**\n * Standard 5-field cron (minute hour day-of-month month day-of-week),\n * validated by `defineIntegration()`. The schema package checks presence\n * only — full syntax validation is the SDK layer's job.\n */\n cron: string;\n handler: (\n ctx: IntegrationContext<Schemas>,\n invocation: ScheduleInvocation,\n ) => Promise<ScheduleResult>;\n}\n\n/**\n * The token set the transport hands `onTokens` after a code exchange (and\n * after each refresh). `raw` carries provider-specific extras verbatim.\n */\nexport interface OAuthTokens {\n accessToken: string;\n refreshToken?: string;\n /** ISO-8601 expiry, when the provider reports one. */\n expiresAt?: string;\n scope?: string;\n tokenType?: string;\n raw?: Record<string, JsonValue>;\n}\n\nexport interface OAuthProviderDeclaration {\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n pkce?: boolean;\n}\n\n/**\n * The OAuth contract. The TRANSPORT owns the flow (authorize redirect, state,\n * callback, code exchange — localhost in the harness, hosted later); the\n * partner declares the provider endpoints, names the client-credential\n * secrets, and persists tokens in `onTokens` via `ctx.secrets` — so token\n * storage policy is the existing capability layer, nothing new.\n * `defineIntegration()` rejects the declaration unless both endpoint origins\n * are covered by the definition's `egress` list.\n */\nexport interface PartnerOAuthDeclaration<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n provider: OAuthProviderDeclaration;\n credentials: {\n clientIdSecretName: string;\n clientSecretSecretName: string;\n };\n onTokens: (\n ctx: IntegrationContext<Schemas>,\n tokens: OAuthTokens,\n ) => Promise<void>;\n}\n\nexport interface IntegrationDefinition<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n slug: string;\n name: string;\n description: string;\n version: string;\n capabilities?: IntegrationCapabilityDeclaration[];\n permissions?: IntegrationPermissionDeclaration[];\n components?: IntegrationComponentDeclarations;\n workspaceTargets?: WorkspaceTargetDefinition[];\n tools?: PartnerToolModule<Schemas>[];\n storage?: Schemas;\n egress?: string[];\n webhooks?: PartnerWebhookDeclaration<Schemas>[];\n schedules?: PartnerScheduleDeclaration<Schemas>[];\n oauth?: PartnerOAuthDeclaration<Schemas>;\n proposes?: IntegrationProposals;\n}\n\n// ---- Shared helpers --------------------------------------------------------\n\nfunction findDuplicates(values: readonly string[]): string[] {\n const seen = new Set<string>();\n const duplicates = new Set<string>();\n for (const value of values) {\n if (seen.has(value)) duplicates.add(value);\n seen.add(value);\n }\n return [...duplicates];\n}\n\nconst HOST_ASSIGNED_REMINDER =\n 'Host-assigned fields are never partner-authorable: productId, kind, trust ' +\n 'tier, and machine exposure (credentialModel) do not exist on ' +\n 'IntegrationDefinition, per-tool effect/sensitivity belong in the ' +\n '`proposes` block, and host-resolved widget fields (productId, ' +\n 'widgetConfigId, workspaceId, collapsed, isPinned, health, ' +\n 'integrationMetadata) are populated by the platform at runtime — remove ' +\n 'them from the definition.';\n\n// ---- Structured findings (non-throwing validation surface) -----------------\n\n/**\n * One structured validation result. This is the committed shape the CLI and\n * any other tooling consumes — the collectors below return arrays of these,\n * and the throwing entry points (`parseIntegrationDefinition`,\n * `validateIntegrationDefinitions`) are built on the exact same rules so there\n * is one rule set, not two.\n *\n * `line` is deliberately optional and usually ABSENT: zod reports a `path`\n * (`components.widgets[2].id`), not a byte offset into a source file, and\n * fabricating a line number would be a lie. The zod path lives in `message`;\n * `file` names the module the definition was loaded from when the caller knows\n * it. `hint` is always a non-empty, imperative remediation instruction.\n */\nexport interface Finding {\n check: string;\n severity: 'error' | 'warn' | 'info';\n file?: string;\n line?: number;\n message: string;\n hint: string;\n}\n\nexport interface CollectDefinitionOptions {\n /** The module the definition was loaded from, stamped onto every finding. */\n file?: string;\n}\n\nconst PLAIN_DATA_HINT =\n 'Declarations must be finite plain data. Remove the cycle, getter/setter, ' +\n 'or class/exotic instance the message names so the value cannot change ' +\n 'after validation, then re-run validate.';\n\n/** Per-issue remediation. Unrecognized keys get the host-assigned reminder. */\nfunction hintForIssue(issue: z.ZodIssue): string {\n if (issue.code === z.ZodIssueCode.unrecognized_keys) {\n return HOST_ASSIGNED_REMINDER;\n }\n const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return (\n `Correct the value at \"${path}\" so it satisfies ` +\n `@ekanos/integration-schema's IntegrationDefinitionSchema, then re-run ` +\n `validate.`\n );\n}\n\n/**\n * The single, shared implementation behind BOTH the non-throwing collector and\n * the throwing `parseIntegrationDefinition`. Runs the plain-data structural\n * check first (short-circuiting exactly as the throwing path always has), then\n * the canonical zod parse. Returns the findings, the sanitized `data` on\n * success, and whether an unrecognized key was among the failures (the\n * throwing path appends the host-assigned reminder only in that case).\n */\nfunction collectDefinitionResult(\n input: unknown,\n options: CollectDefinitionOptions = {},\n): {\n findings: Finding[];\n data?: IntegrationDefinition;\n hasUnrecognizedKey: boolean;\n} {\n const { file } = options;\n\n // Structural pre-check: the same rule assertPlainDeclaration throws on, but\n // captured as a finding. If it fails we stop here, matching the throwing\n // path which never reaches safeParse once the pre-check throws.\n try {\n assertPlainDeclaration(input);\n } catch (error) {\n return {\n findings: [\n {\n check: 'definition.plain-data',\n severity: 'error',\n ...(file ? { file } : {}),\n message: error instanceof Error ? error.message : String(error),\n hint: PLAIN_DATA_HINT,\n },\n ],\n hasUnrecognizedKey: false,\n };\n }\n\n const result = IntegrationDefinitionSchema.safeParse(input);\n if (result.success) {\n deepFreezeDefinition(result.data);\n return {\n findings: [],\n data: result.data as unknown as IntegrationDefinition,\n hasUnrecognizedKey: false,\n };\n }\n\n const hasUnrecognizedKey = result.error.issues.some(\n (issue) => issue.code === z.ZodIssueCode.unrecognized_keys,\n );\n\n const findings = result.error.issues.map((issue): Finding => {\n const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return {\n check: 'definition.schema',\n severity: 'error',\n ...(file ? { file } : {}),\n // The zod path, not a source line — see the Finding doc comment.\n message: `${path}: ${issue.message}`,\n hint: hintForIssue(issue),\n };\n });\n\n return { findings, hasUnrecognizedKey };\n}\n\n/**\n * Non-throwing sibling of `parseIntegrationDefinition`: validates a single\n * integration definition against the canonical schema and returns structured\n * findings instead of throwing a pre-formatted string. An empty array means\n * the definition is valid. Used by `@ekanos/cli validate`.\n */\nexport function collectDefinitionFindings(\n input: unknown,\n options: CollectDefinitionOptions = {},\n): Finding[] {\n return collectDefinitionResult(input, options).findings;\n}\n\n/**\n * A zod schema (storage leaf) or a React exotic (`memo`/`forwardRef`, an\n * object tagged with `$$typeof`). NOTHING here reads a property value:\n * `instanceof` walks the prototype chain and `in` is [[HasProperty]], so a\n * malicious getter still cannot execute during leaf detection (F4).\n *\n * `instanceof z.ZodType` alone is NOT sufficient. It answers \"is this an\n * instance of THIS package's zod copy\", and a partner's `zod` is routinely a\n * different copy — any transitive dependency pinning a different range, or a\n * package manager that nests instead of deduping, is enough. When the copies\n * differ, `instanceof` is false, `assertPlainDeclaration` walks INTO the\n * schema, and every storage key is rejected as \"a class/exotic instance, not\n * a plain object\" — an error that says nothing about the real cause and sends\n * the author looking at an object literal that is already correct.\n *\n * So structural detection is the fallback, matching the duck-typing\n * `isZodSchemaLike` already does one layer down for exactly this reason.\n * `~standard` is the Standard Schema marker (zod >= 3.24); `_def` +\n * `safeParse` covers older copies. This widens nothing security-relevant: a\n * value that clears this check still has to satisfy `zodSchemaRef` /\n * `storageKeyDeclarationRef`, which call `.safeParse` regardless, and the\n * re-parse at the host trust boundary remains the real guard.\n */\nfunction isDeclarationLeaf(value: object): boolean {\n if (value instanceof z.ZodType) return true;\n if ('$$typeof' in value) return true;\n\n return '~standard' in value || ('_def' in value && 'safeParse' in value);\n}\n\n/**\n * F4: reject accessor/proxy/class-instance/cyclic declaration containers\n * before parsing. An object with getters (or a proxy) can return validated\n * values during parse and different values later; a non-plain prototype can\n * smuggle mutable state past `z.object()`; a cycle would recurse into zod\n * rather than fail cleanly. We walk every CONTAINER (plain object / array),\n * and stop at legitimate leaves: functions (component refs, `run`) and zod\n * schemas (storage). Leaf detection happens BEFORE any own-property read, so\n * a `safeParse` getter cannot execute. Proxy detection is best-effort — the\n * re-parse at the host boundary (which materializes fresh values via zod) is\n * the real guard.\n *\n * Cycle detection tracks the ANCESTOR chain only (add on enter, remove on\n * exit): a genuine back-edge is a cycle, but the same object referenced from\n * two sibling branches (a DAG — e.g. a shallow-cloned widget sharing a\n * `layouts` object) is not, and must not be rejected.\n */\nexport function assertPlainDeclaration(\n value: unknown,\n path = '(root)',\n ancestors: WeakSet<object> = new WeakSet(),\n): void {\n if (value === null || typeof value !== 'object') return;\n\n if (typeof value === 'function') return;\n\n // Leaf detection first — instanceof / HasProperty never invoke a getter.\n if (isDeclarationLeaf(value)) return;\n\n if (ancestors.has(value)) {\n throw new Error(\n `Integration definition contains a cycle at ${path}. ` +\n `Declarations must be finite plain data — remove the self-reference.`,\n );\n }\n\n const proto = Object.getPrototypeOf(value) as unknown;\n const isArray = Array.isArray(value);\n if (!isArray && proto !== Object.prototype && proto !== null) {\n throw new Error(\n `Integration definition value at ${path} is a class/exotic instance, not a plain object. ` +\n `Declaration containers must be plain object/array literals so their values cannot mutate after validation.`,\n );\n }\n\n ancestors.add(value);\n for (const key of Object.keys(value)) {\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (descriptor && (descriptor.get || descriptor.set)) {\n throw new Error(\n `Integration definition property at ${path}.${key} is a getter/setter, not a data property. ` +\n `Declaration values must be plain data — a getter can return a different value after validation.`,\n );\n }\n assertPlainDeclaration(\n (value as Record<string, unknown>)[key],\n `${path}.${key}`,\n ancestors,\n );\n }\n ancestors.delete(value);\n}\n\nconst frozen = new WeakSet<object>();\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') return false;\n const proto = Object.getPrototypeOf(value) as unknown;\n return proto === Object.prototype || proto === null;\n}\n\n/**\n * Cycle-aware deep freeze (F4). Freezes plain objects and arrays; leaves zod\n * schemas (freezing breaks their internal caches) and functions alone.\n */\nexport function deepFreezeDefinition(value: unknown): void {\n if (value === null || typeof value !== 'object') return;\n if (frozen.has(value)) return;\n\n if (Array.isArray(value)) {\n frozen.add(value);\n Object.freeze(value);\n for (const item of value) deepFreezeDefinition(item);\n return;\n }\n if (isPlainObject(value)) {\n frozen.add(value);\n Object.freeze(value);\n for (const item of Object.values(value)) deepFreezeDefinition(item);\n }\n}\n\n/**\n * The single validation entry point used by BOTH `defineIntegration()` (SDK,\n * authoring time) and `registerPartnerIntegration()` (core, host trust\n * boundary — F3). Rejects non-plain containers, then parses against the\n * canonical schema, and returns the SANITIZED `result.data` (fresh, plain,\n * strict-stripped — never the caller's original object). Throws an Error\n * whose message is a remediation instruction.\n */\nexport function parseIntegrationDefinition(\n input: unknown,\n): IntegrationDefinition {\n // Built ON TOP of the collector so there is exactly one rule set. The\n // collector runs assertPlainDeclaration first (short-circuiting), then the\n // canonical parse, and freezes `data` on success (F4) — so a successful\n // result here is already the deep-frozen, sanitized definition.\n const { findings, data, hasUnrecognizedKey } = collectDefinitionResult(input);\n\n if (data) {\n // The schema's inferred output matches IntegrationDefinition in every\n // non-generic field; the storage/tool generics default to the permissive\n // base, which is exactly right for the loose registration boundary.\n return data;\n }\n\n // A plain-data structural failure is thrown verbatim (its message is already\n // a remediation: \"…contains a cycle…\", \"…is a getter/setter…\").\n const plainDataFinding = findings.find(\n (finding) => finding.check === 'definition.plain-data',\n );\n if (plainDataFinding) {\n throw new Error(plainDataFinding.message);\n }\n\n // Schema failures reproduce the historical message shape exactly: each\n // finding's message is already `${path}: ${issue.message}`, so re-prefixing\n // with ` - ` reconstructs formatIssues() verbatim.\n const slug =\n typeof (input as { slug?: unknown } | null)?.slug === 'string'\n ? ` for \"${(input as { slug: string }).slug}\"`\n : '';\n // The reminder is six lines about fields the author may not have written.\n // Appending it to EVERY failure buries the one line that matters — a bad\n // semver or a malformed cron arrives under a paragraph about productId and\n // trust tiers. Show it only when an unrecognized key is what failed, which\n // is the case it was written for.\n throw new Error(\n `Invalid integration definition${slug}:\\n` +\n findings.map((finding) => ` - ${finding.message}`).join('\\n') +\n (hasUnrecognizedKey ? `\\n${HOST_ASSIGNED_REMINDER}` : ''),\n );\n}\n\n// ---- Cross-definition collision detection (F5) -----------------------------\n\n/**\n * THE canonical effective MCP tool name — the single source of truth for how\n * discovery keys a tool. Tool discovery namespaces each raw tool name with the\n * integration slug (`slug.replace(/-/g,'_')`), skipping the prefix when the\n * name already carries it. Two collision-free RAW pairs can therefore collapse\n * to the same EFFECTIVE name (`{slug:\"foo\",tool:\"bar_baz\"}` and\n * `{slug:\"foo-bar\",tool:\"baz\"}` both become `foo_bar_baz`), so collision\n * checking MUST compare effective names, and runtime discovery MUST throw on a\n * duplicate assignment. Both call this one helper\n * (`packages/agents/src/tools/tool-discovery.ts`).\n */\nexport function getDiscoveredToolName(slug: string, rawName: string): string {\n const slugPrefix = slug.replace(/-/g, '_');\n return rawName.startsWith(slugPrefix) ? rawName : `${slugPrefix}_${rawName}`;\n}\n\n/**\n * The metadata-only shape collision checking needs — no handlers, no schemas\n * (F8: collision validation must not force widening the tool handlers). A\n * full `IntegrationDefinition` is assignable to it.\n */\nexport interface DefinitionCollisionInput {\n slug: string;\n components?: { widgets?: readonly { id: string }[] } | null;\n tools?: readonly { name: string }[] | null;\n}\n\n/**\n * First-party identifiers a partner definition must not collide with (F5).\n * Widget lookup matches on `widget.id` and the assistant's tool list is a\n * flat name-keyed map where a later registration overwrites an earlier one,\n * so a partner reusing a first-party id/name silently hijacks it. Sourced\n * from the reviewed registry inventory, not from executing partner handlers.\n */\nexport interface FirstPartyInventory {\n slugs?: readonly string[];\n widgetIds?: readonly string[];\n /**\n * EFFECTIVE (discovery) tool names — `getDiscoveredToolName(slug, rawName)` —\n * NOT raw names, since discovery keys the flat registry by the effective name.\n */\n toolNames?: readonly string[];\n}\n\n/**\n * Detects cross-definition collisions (duplicate slugs, widget ids, tool\n * names across the partner set) AND collisions against the first-party\n * inventory, throwing one error listing EVERY collision.\n *\n * This is the build-time gate the host registry deliberately lacks:\n * `integrationRegistry.register()` keys by slug via `Map.set` and silently\n * OVERWRITES, and duplicate widget/tool ids resolve last- or\n * first-registration-wins by import order.\n */\n/**\n * One collision, split into the human `message` (unchanged from the strings\n * this module has always produced — callers and tests match on fragments like\n * `effective tool name \"foo_bar_baz\"`) and a separate imperative `hint`.\n */\ninterface CollisionRecord {\n check: string;\n message: string;\n hint: string;\n}\n\n/**\n * THE shared collision rule set, returning structured records. Both the\n * throwing `validateIntegrationDefinitions` and the non-throwing\n * `collectCollisionFindings` are built on this, so there is one rule set.\n */\nfunction computeCollisionRecords(\n definitions: readonly DefinitionCollisionInput[],\n firstParty: FirstPartyInventory = {},\n): CollisionRecord[] {\n const slugCounts = new Map<string, number>();\n const widgetOwners = new Map<string, string[]>();\n const toolOwners = new Map<string, string[]>();\n\n for (const definition of definitions) {\n slugCounts.set(definition.slug, (slugCounts.get(definition.slug) ?? 0) + 1);\n for (const widget of definition.components?.widgets ?? []) {\n widgetOwners.set(widget.id, [\n ...(widgetOwners.get(widget.id) ?? []),\n definition.slug,\n ]);\n }\n for (const tool of definition.tools ?? []) {\n // EFFECTIVE (discovery) name, not the raw name — two collision-free raw\n // names can collapse to the same effective key.\n const effective = getDiscoveredToolName(definition.slug, tool.name);\n toolOwners.set(effective, [\n ...(toolOwners.get(effective) ?? []),\n definition.slug,\n ]);\n }\n }\n\n const records: CollisionRecord[] = [];\n\n for (const [slug, count] of slugCounts) {\n if (count > 1) {\n records.push({\n check: 'collision.slug',\n message: `slug \"${slug}\" is declared by ${count} partner definitions — slugs are the registry key and must be globally unique.`,\n hint: `Rename all but one of the definitions declaring slug \"${slug}\" so every slug is globally unique.`,\n });\n }\n }\n for (const [id, owners] of widgetOwners) {\n if (owners.length > 1) {\n records.push({\n check: 'collision.widget-id',\n message: `widget id \"${id}\" is declared by [${owners.join(', ')}] — widget ids are global (widget_config rows key on them); prefix yours with the integration slug.`,\n hint: `Prefix widget id \"${id}\" with your integration slug so it is globally unique.`,\n });\n }\n }\n for (const [name, owners] of toolOwners) {\n if (owners.length > 1) {\n records.push({\n check: 'collision.tool-name',\n message: `effective tool name \"${name}\" is declared by [${owners.join(', ')}] — discovery namespaces tool names by slug, so these collapse to one flat key and overwrite each other. Rename so the slug-prefixed names differ.`,\n hint: `Rename the colliding tools so their slug-prefixed effective names differ from \"${name}\".`,\n });\n }\n }\n\n const reservedSlugs = new Set(firstParty.slugs ?? []);\n const reservedWidgets = new Set(firstParty.widgetIds ?? []);\n const reservedTools = new Set(firstParty.toolNames ?? []);\n\n for (const [slug, owners] of groupOwners(definitions, (d) => [d.slug])) {\n if (reservedSlugs.has(slug)) {\n records.push({\n check: 'collision.reserved-slug',\n message: `slug \"${slug}\" (declared by [${owners.join(', ')}]) collides with a first-party integration — pick a slug no built-in product uses.`,\n hint: `Choose a different slug than \"${slug}\" — it is reserved by a first-party integration.`,\n });\n }\n }\n for (const [id, owners] of widgetOwners) {\n if (reservedWidgets.has(id)) {\n records.push({\n check: 'collision.reserved-widget-id',\n message: `widget id \"${id}\" (declared by [${owners.join(', ')}]) collides with a first-party widget — the dashboard resolves widgets by id, so this would hijack it. Prefix with the integration slug.`,\n hint: `Prefix widget id \"${id}\" with your integration slug — it is reserved by a first-party widget.`,\n });\n }\n }\n for (const [name, owners] of toolOwners) {\n if (reservedTools.has(name)) {\n records.push({\n check: 'collision.reserved-tool-name',\n message: `effective tool name \"${name}\" (declared by [${owners.join(', ')}]) collides with a first-party tool — the flat, slug-namespaced tool registry would overwrite one with the other. Rename it.`,\n hint: `Rename the tool so its effective name is not \"${name}\" — that name is reserved by a first-party tool.`,\n });\n }\n }\n\n return records;\n}\n\n/**\n * Non-throwing sibling of `validateIntegrationDefinitions`: returns structured\n * collision findings across the partner set (and against the first-party\n * inventory) instead of throwing. An empty array means no collisions. Used by\n * `@ekanos/cli validate`.\n */\nexport function collectCollisionFindings(\n definitions: readonly DefinitionCollisionInput[],\n firstParty: FirstPartyInventory = {},\n): Finding[] {\n return computeCollisionRecords(definitions, firstParty).map(\n (record): Finding => ({\n check: record.check,\n severity: 'error',\n message: record.message,\n hint: record.hint,\n }),\n );\n}\n\nexport function validateIntegrationDefinitions(\n definitions: readonly DefinitionCollisionInput[],\n firstParty: FirstPartyInventory = {},\n): void {\n const collisions = computeCollisionRecords(definitions, firstParty).map(\n (record) => record.message,\n );\n\n if (collisions.length > 0) {\n throw new Error(\n `Integration definitions collide (${collisions.length} collision${collisions.length === 1 ? '' : 's'}):\\n` +\n collisions.map((line) => ` - ${line}`).join('\\n') +\n '\\nRename until every slug, widget id, and tool name is unique — the host registry would otherwise silently overwrite or drop a registration.',\n );\n }\n}\n\nfunction groupOwners(\n definitions: readonly DefinitionCollisionInput[],\n keysOf: (definition: DefinitionCollisionInput) => string[],\n): Map<string, string[]> {\n const owners = new Map<string, string[]>();\n for (const definition of definitions) {\n for (const key of keysOf(definition)) {\n owners.set(key, [...(owners.get(key) ?? []), definition.slug]);\n }\n }\n return owners;\n}\n"]}
1
+ {"version":3,"file":"integration-definition.js","sourceRoot":"","sources":["../src/integration-definition.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The canonical integration-definition contract: ONE zod schema and ONE set\n * of inferred/hand-written types (F9). `@ekanos/sdk`'s `defineIntegration()`\n * validates against this schema at authoring time; `@kit/integrations-core`'s\n * `registerPartnerIntegration()` re-validates against the SAME schema at the\n * host trust boundary (F3). Both packages depend on this one; it depends on\n * neither, so there is no cycle and no hand-written structural twin.\n *\n * Dependency-pure (zod only): partner component refs are checked\n * structurally via `ComponentReference`, so no React dependency leaks in.\n */\nimport { z } from 'zod';\n\nimport type {\n IntegrationContext,\n StorageKeyDeclarationInput,\n StorageSchemas,\n} from './capability-context';\nimport type { ComponentReference } from './component-reference';\nimport { isComponentReference } from './component-reference';\nimport {\n type WorkspaceTargetDefinition,\n WorkspaceTargetListSchema,\n} from './workspace-target';\n\n// ---- JSON-compatible values (F4) ------------------------------------------\n\ntype JsonPrimitive = string | number | boolean | null;\nexport type JsonValue =\n JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };\n\nconst jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>\n z.union([\n z.string(),\n // `.finite()` rejects Infinity/-Infinity AND NaN (Number.isFinite) — all\n // of which JSON.stringify silently turns to `null`, so they are not\n // JSON-compatible values (F4).\n z.number().finite(),\n z.boolean(),\n z.null(),\n z.array(jsonValueSchema),\n z.record(jsonValueSchema),\n ]),\n);\n\n// ---- Leaf validators -------------------------------------------------------\n\nfunction componentRefSchema(what: string) {\n return z.custom<ComponentReference>(isComponentReference, {\n message: `${what} must be a React component reference (a function component, or a memo/forwardRef/lazy wrapper) — pass the component itself, not an element or a module path.`,\n });\n}\n\nconst zodSchemaRef = z.custom<z.ZodType>(\n (value) =>\n typeof (value as { safeParse?: unknown } | null)?.safeParse === 'function',\n {\n message:\n 'Every storage key must declare a zod schema (e.g. z.object({ … })) — ctx.storage validates reads and writes against it.',\n },\n);\n\nconst nonEmpty = (what: string) =>\n z.string().min(1, { message: `${what} must be a non-empty string.` });\n\nconst slugSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {\n message:\n 'slug must be kebab-case ([a-z0-9] segments separated by single hyphens), e.g. \"acme-crm\" — it becomes the product slug, route segment, and MCP namespace.',\n});\n\nconst widgetIdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {\n message:\n 'Widget ids are kebab-case and globally unique, e.g. \"acme-crm-pipeline\" — prefix with the integration slug to stay collision-free.',\n});\n\n/**\n * The `data_type` values each storage scope permits.\n *\n * THIS IS THE SOURCE OF TRUTH, and it lives here rather than in the host\n * because this package is the one a partner's `defineIntegration()` validates\n * against — a declaration naming an impossible data_type should fail at\n * authoring time, not on the first `ctx.storage` call in production. The host\n * (`apps/web/lib/server/integration-context.ts`) imports these same arrays for\n * its runtime check, so the two cannot drift.\n *\n * They mirror the DB CHECK constraints on `account_product_data` and\n * `user_product_data` (`apps/web/supabase/schemas/26-integrations.sql`), with\n * one deliberate subtraction: see `RESERVED_STORAGE_DATA_TYPES`.\n */\nexport const ACCOUNT_STORAGE_DATA_TYPES = [\n 'activation',\n 'settings',\n 'metrics_summary',\n 'sync_state',\n 'cache',\n] as const;\n\nexport const USER_STORAGE_DATA_TYPES = [\n 'config',\n 'preferences',\n 'cache',\n] as const;\n\n/**\n * In the DB CHECK but NEVER addressable through `ctx.storage`.\n *\n * `secret` rows are host-managed per-name credential rows whose values live in\n * Vault; partner code reaches them only through `ctx.secrets`, which never\n * exposes a value or a vault id. Letting a partner DECLARE `secret` storage\n * would hand them a key that collides with host-managed secret rows, so it is\n * rejected here with its own message rather than the generic \"not an allowed\n * data_type\" — an author who wrote `secret` meant something specific and needs\n * to be pointed at `ctx.secrets`.\n *\n * Kept as its own list rather than simply omitted from the arrays above so the\n * reason survives: `secret` is a real column value, not a typo.\n */\nexport const RESERVED_STORAGE_DATA_TYPES = ['secret'] as const;\n\n/** VARCHAR(100) on both the data_type and data_subtype columns. */\nconst MAX_STORAGE_SEGMENT_LENGTH = 100;\n\nconst STORAGE_KEY_SHAPE = /^[a-z0-9_-]+(?:\\/[a-z0-9_-]+)?$/;\n\n/**\n * A storage key is `\"<dataType>\"` or `\"<dataType>/<subtype>\"`, mapped onto the\n * `(data_type, data_subtype)` columns. Scope-specific because the two tables\n * carry different CHECK constraints — `settings` is an account data_type and\n * `preferences` a user one, and neither is valid in the other's scope.\n */\nfunction storageKeySchemaFor(scope: 'account' | 'user') {\n const allowed: readonly string[] =\n scope === 'account' ? ACCOUNT_STORAGE_DATA_TYPES : USER_STORAGE_DATA_TYPES;\n\n return z.string().superRefine((key, ctx) => {\n const fail = (message: string) =>\n ctx.addIssue({ code: z.ZodIssueCode.custom, message });\n\n if (!STORAGE_KEY_SHAPE.test(key)) {\n fail(\n `Storage key \"${key}\" is malformed. Keys are \"<dataType>\" or ` +\n `\"<dataType>/<subtype>\" in lowercase [a-z0-9_-], with at most one ` +\n `\"/\" and no empty segment.`,\n );\n return;\n }\n\n const slashIndex = key.indexOf('/');\n const dataType = slashIndex === -1 ? key : key.slice(0, slashIndex);\n const dataSubtype =\n slashIndex === -1 ? undefined : key.slice(slashIndex + 1);\n\n if ((RESERVED_STORAGE_DATA_TYPES as readonly string[]).includes(dataType)) {\n fail(\n `Storage key \"${key}\" uses the reserved \"${dataType}\" data_type, ` +\n `which ctx.storage can never read or write. Integration secrets are ` +\n `host-managed — declare nothing here and use ctx.secrets.get/set/names ` +\n `instead; a secret's value and its vault id are never reachable ` +\n `through ctx.storage.`,\n );\n return;\n }\n\n if (!allowed.includes(dataType)) {\n fail(\n `Storage key \"${key}\" is invalid for the ${scope} scope: ` +\n `\"${dataType}\" is not an allowed ${scope} data_type. Use one of ` +\n `[${allowed.join(', ')}] (the database CHECK constraint).`,\n );\n return;\n }\n\n if (\n dataSubtype !== undefined &&\n dataSubtype.length > MAX_STORAGE_SEGMENT_LENGTH\n ) {\n fail(\n `Storage key \"${key}\" has a subtype longer than ` +\n `${MAX_STORAGE_SEGMENT_LENGTH} characters (the column width).`,\n );\n }\n });\n}\n\nconst toolNameSchema = z.string().regex(/^[a-z][a-z0-9_]*$/, {\n message:\n 'Tool names are lowercase snake_case starting with a letter, e.g. \"list_invoices\" — the model calls them by this exact string.',\n});\n\nconst webhookIdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {\n message:\n 'Webhook ids are kebab-case, e.g. \"payment-updated\" — the host ingress route addresses the handler by this exact string.',\n});\n\nconst scheduleIdSchema = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {\n message:\n 'Schedule ids are kebab-case, e.g. \"daily-reconcile\" — the host scheduler addresses the handler by this exact string.',\n});\n\n// Origin-only egress entries (ruling 3): absolute https origins, optional\n// `*.` subdomain wildcard, no path/query/hash/credentials/http. Kept as a\n// pure regex here so this package depends on nothing; the SDK's shared\n// `parseEgressEntry`/`isEgressAllowed` matcher enforces the identical shape\n// at runtime.\nconst egressEntrySchema = z.string().superRefine((entry, ctx) => {\n const match = /^https:\\/\\/(\\*\\.)?([a-z0-9.-]+)(?::(\\d+))?$/i.exec(entry);\n if (!match) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n `Egress entry \"${entry}\" is invalid. Entries are https origins only — ` +\n `\"https://api.example.com\" (exact) or \"https://*.example.com\" ` +\n `(subdomain wildcard): scheme + host + optional port, no path, query, ` +\n `hash, credentials, or http. Fix it in the integration's egress list.`,\n });\n return;\n }\n const host = match[2] ?? '';\n if (host.includes('*') || host.startsWith('.') || host.endsWith('.')) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Egress entry \"${entry}\" has a malformed host — wildcards are only supported as a single leading \"*.\" (e.g. \"https://*.example.com\").`,\n });\n }\n});\n\n// ---- Sub-object schemas ----------------------------------------------------\n\nconst capabilitySchema = z\n .object({\n label: nonEmpty('capabilities[].label'),\n description: nonEmpty('capabilities[].description'),\n icon: componentRefSchema('capabilities[].icon').optional(),\n })\n .strict();\n\nconst permissionSchema = z\n .object({\n label: nonEmpty('permissions[].label'),\n detail: nonEmpty('permissions[].detail'),\n type: z.enum(['read', 'write']),\n })\n .strict();\n\nconst gridUnitSchema = z\n .object({\n cols: z.number().int().positive(),\n rows: z.number().int().positive(),\n })\n .strict();\n\nconst layoutBoxSchema = z\n .object({\n x: z.number(),\n y: z.number(),\n w: z.number(),\n h: z.number(),\n maxHeight: z.number().optional(),\n })\n .strict();\n\n/**\n * F7: authorable widget fields ONLY. Host-resolved fields (`productId`,\n * `widgetConfigId`, `workspaceId`, `collapsed`, `isPinned`, `health`,\n * `integrationMetadata`) are absent by design and rejected at runtime by\n * `.strict()` — the type and the runtime agree. The host adapter maps this\n * into the full `WidgetConfig`.\n */\nconst widgetSchema = z\n .object({\n id: widgetIdSchema,\n name: nonEmpty('widgets[].name'),\n component: componentRefSchema('widgets[].component'),\n widgetState: z.enum(['active', 'inactive', 'disabled']),\n gridSize: z.union([gridUnitSchema, z.array(gridUnitSchema)]).optional(),\n gridPosition: z\n .object({ col: z.number(), row: z.number() })\n .strict()\n .optional(),\n layouts: z\n .object({\n lg: layoutBoxSchema.optional(),\n md: layoutBoxSchema.optional(),\n sm: layoutBoxSchema.optional(),\n })\n .strict()\n .optional(),\n category: z\n .object({\n id: z.string(),\n name: z.string(),\n slug: z.string(),\n icon: z.string().nullable(),\n })\n .strict()\n .optional(),\n isCollapsible: z.boolean().optional(),\n isPinnable: z.boolean().optional(),\n aiFooterEnabled: z.boolean().optional(),\n })\n .strict();\n\nconst toolParametersSchema = z\n .object({\n type: z.literal('object'),\n properties: z.record(jsonValueSchema).optional(),\n required: z.array(z.string()).optional(),\n additionalProperties: z.boolean().optional(),\n })\n .strict();\n\nconst toolRunSchema = z.custom<\n (ctx: never, args: Record<string, unknown>) => Promise<unknown>\n>((value) => typeof value === 'function', {\n message:\n 'tools[].run must be a function (ctx, args) => Promise<result> — it receives the host-scoped IntegrationContext, never a raw client.',\n});\n\nconst toolSchema = z\n .object({\n name: toolNameSchema,\n description: nonEmpty('tools[].description'),\n parameters: toolParametersSchema.optional(),\n run: toolRunSchema,\n outputExample: z.record(jsonValueSchema).optional(),\n })\n .strict();\n\n// ---- Event surfaces (webhooks, schedules, OAuth) ---------------------------\n//\n// Declared exactly like MCP tools: metadata parsed strictly, handlers checked\n// structurally as functions (`z.custom`) and carried through the parse\n// untouched. The declarations are the CONTRACT; every transport — the local\n// harness today, the host's public ingress/scheduler/hosted-callback later —\n// binds to these same fields, so a partner package never changes when the\n// real transports arrive.\n\nfunction handlerSchema(what: string, shape: string) {\n return z.custom<(ctx: never, arg: never) => Promise<unknown>>(\n (value) => typeof value === 'function',\n {\n message: `${what} must be a function ${shape} — it receives the host-scoped IntegrationContext, never a raw request or client.`,\n },\n );\n}\n\nconst payloadSchemaRef = z.custom<z.ZodType>(\n (value) =>\n typeof (value as { safeParse?: unknown } | null)?.safeParse === 'function',\n {\n message:\n 'webhooks[].payloadSchema must be a zod schema (e.g. z.object({ … })) — the transport validates every delivery against it before the handler runs.',\n },\n);\n\n/**\n * How the TRANSPORT verifies a delivery. Verification is never the partner's\n * job: the declaration names the signature header and the secret that signs\n * it; the host ingress enforces it (the local harness logs it as skipped).\n * `'none'` is an explicit statement that the source is unsigned.\n */\nconst webhookSignatureSchema = z.union([\n z.literal('none'),\n z\n .object({\n header: nonEmpty('webhooks[].signature.header'),\n secretName: nonEmpty('webhooks[].signature.secretName'),\n })\n .strict(),\n]);\n\nconst webhookSchema = z\n .object({\n id: webhookIdSchema,\n description: nonEmpty('webhooks[].description'),\n payloadSchema: payloadSchemaRef,\n signature: webhookSignatureSchema,\n examplePayload: z.record(jsonValueSchema).optional(),\n handler: handlerSchema(\n 'webhooks[].handler',\n '(ctx, event) => Promise<WebhookResult>',\n ),\n })\n .strict();\n\nconst scheduleSchema = z\n .object({\n id: scheduleIdSchema,\n description: nonEmpty('schedules[].description'),\n // Presence only here — the dependency-pure schema package stays zod-only,\n // so the real 5-field cron syntax check lives in the SDK layer\n // (`defineIntegration()`), the same way it layers cross-field rules today.\n cron: nonEmpty('schedules[].cron'),\n handler: handlerSchema(\n 'schedules[].handler',\n '(ctx, invocation) => Promise<ScheduleResult>',\n ),\n })\n .strict();\n\nconst httpsUrlSchema = (what: string) =>\n z.string().superRefine((value, ctx) => {\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${what} must be an absolute URL, e.g. \"https://provider.example/oauth/authorize\".`,\n });\n return;\n }\n if (parsed.protocol !== 'https:') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${what} must use https — OAuth endpoints are never plain http.`,\n });\n }\n if (parsed.username !== '' || parsed.password !== '') {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${what} must not embed credentials.`,\n });\n }\n });\n\nconst oauthSchema = z\n .object({\n provider: z\n .object({\n authorizationUrl: httpsUrlSchema('oauth.provider.authorizationUrl'),\n tokenUrl: httpsUrlSchema('oauth.provider.tokenUrl'),\n scopes: z.array(nonEmpty('oauth.provider.scopes[]')),\n pkce: z.boolean().optional(),\n })\n .strict(),\n credentials: z\n .object({\n clientIdSecretName: nonEmpty('oauth.credentials.clientIdSecretName'),\n clientSecretSecretName: nonEmpty(\n 'oauth.credentials.clientSecretSecretName',\n ),\n })\n .strict(),\n onTokens: handlerSchema('oauth.onTokens', '(ctx, tokens) => Promise<void>'),\n })\n .strict();\n\n// ---- Activation lifecycle hook ---------------------------------------------\n//\n// Declared and checked exactly like the other handler-bearing fields above:\n// structural function check via `handlerSchema`, carried through the parse\n// untouched. See the `onActivate` TSDoc on `IntegrationDefinition` for the\n// full semantics (when it runs, why v1 errors are non-fatal).\nconst onActivateSchema = handlerSchema('onActivate', '(ctx) => Promise<void>');\n\nconst toolClassificationProposalSchema = z\n .object({\n effect: z.enum(['read', 'write']).optional(),\n sensitivity: z.enum(['public', 'internal', 'pii', 'financial']).optional(),\n })\n .strict();\n\nconst proposalsSchema = z\n .object({\n credentialModel: z.enum(['account', 'user', 'source']).optional(),\n tools: z.record(toolClassificationProposalSchema).optional(),\n })\n .strict();\n\n/**\n * A storage key's EXPLICIT declaration: the zod schema plus its exposure\n * flags. `clientReadable` is the only way a declared key becomes readable by\n * the browser through the generic storage route — and it defaults to false,\n * so the bare-schema form stays server-only exactly as it always was.\n * `.strict()` keeps an unrecognized flag (a typo like `clientReadible`) an\n * error rather than a silently-ignored key whose author believes it is\n * exposed — or, worse, believes it is not.\n */\nconst storageKeyDeclarationSchema = z\n .object({\n schema: zodSchemaRef,\n clientReadable: z.boolean().optional(),\n })\n .strict();\n\n/** Duck-typed so a partner's own bundled zod copy still reads as a schema. */\nfunction isZodSchemaLike(value: unknown): boolean {\n return (\n typeof (value as { safeParse?: unknown } | null)?.safeParse === 'function'\n );\n}\n\n/**\n * Either declaration form, hand-routed rather than expressed as `z.union` so\n * the failure message stays specific. A union reports a bare \"Invalid input\"\n * for every wrong shape, which would lose both the \"declare a zod schema\"\n * guidance AND the strict-descriptor typo report — the two errors an author\n * is actually going to hit.\n */\nconst storageKeyDeclarationRef = z\n .custom<StorageKeyDeclarationInput>(() => true)\n .superRefine((value, ctx) => {\n if (isZodSchemaLike(value)) return;\n\n const looksLikeDescriptor =\n value !== null && typeof value === 'object' && !Array.isArray(value);\n\n // Neither form: name both, since the descriptor is the less obvious one.\n if (\n !looksLikeDescriptor ||\n !isZodSchemaLike((value as { schema?: unknown }).schema)\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'Every storage key must declare either a zod schema (e.g. z.object({ … })) or a { schema, clientReadable } descriptor — ctx.storage validates reads and writes against the schema, and clientReadable (default false) is what opts the key in to the browser-readable storage route.',\n });\n return;\n }\n\n // A real descriptor with a real schema — report its own issues verbatim\n // (an unrecognized flag, a non-boolean clientReadable) rather than the\n // generic message, which would send the author looking in the wrong place.\n const result = storageKeyDeclarationSchema.safeParse(value);\n if (result.success) return;\n\n for (const issue of result.error.issues) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: issue.path,\n message: issue.message,\n });\n }\n });\n\nconst storageScopeSchemaFor = (scope: 'account' | 'user') =>\n z.record(storageKeySchemaFor(scope), storageKeyDeclarationRef);\n\nconst componentsSchema = z\n .object({\n activationForm: componentRefSchema('components.activationForm').optional(),\n marketplaceTile: componentRefSchema(\n 'components.marketplaceTile',\n ).optional(),\n widgets: z.array(widgetSchema).optional(),\n })\n .strict();\n\n/**\n * THE canonical schema. Strict everywhere: an unrecognized key is an error,\n * which is what keeps host-assigned fields (productId, kind, trust tier,\n * credentialModel, per-tool effect/sensitivity, host-resolved widget fields)\n * structurally un-settable at runtime, not merely absent from the type.\n */\nexport const IntegrationDefinitionSchema = z\n .object({\n slug: slugSchema,\n name: nonEmpty('name'),\n description: nonEmpty('description'),\n version: z\n .string()\n .regex(\n /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/,\n {\n message:\n 'version must be semver (\"1.0.0\", optionally with a prerelease/build suffix) — promotion diffs definitions by it.',\n },\n ),\n capabilities: z.array(capabilitySchema).optional(),\n permissions: z.array(permissionSchema).optional(),\n components: componentsSchema.optional(),\n workspaceTargets: WorkspaceTargetListSchema.optional(),\n tools: z.array(toolSchema).optional(),\n storage: z\n .object({\n account: storageScopeSchemaFor('account').optional(),\n user: storageScopeSchemaFor('user').optional(),\n })\n .strict()\n .optional(),\n egress: z.array(egressEntrySchema).optional(),\n webhooks: z.array(webhookSchema).optional(),\n schedules: z.array(scheduleSchema).optional(),\n oauth: oauthSchema.optional(),\n onActivate: onActivateSchema.optional(),\n proposes: proposalsSchema.optional(),\n })\n .strict()\n .superRefine((definition, ctx) => {\n const webhookIds = (definition.webhooks ?? []).map((w) => w.id);\n for (const id of findDuplicates(webhookIds)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['webhooks'],\n message: `Webhook id \"${id}\" is declared more than once — give every webhook a unique id.`,\n });\n }\n\n const scheduleIds = (definition.schedules ?? []).map((s) => s.id);\n for (const id of findDuplicates(scheduleIds)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['schedules'],\n message: `Schedule id \"${id}\" is declared more than once — give every schedule a unique id.`,\n });\n }\n\n const widgetIds = (definition.components?.widgets ?? []).map((w) => w.id);\n for (const id of findDuplicates(widgetIds)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['components', 'widgets'],\n message: `Widget id \"${id}\" is declared more than once — give every widget a unique id.`,\n });\n }\n\n const toolNames = (definition.tools ?? []).map((tool) => tool.name);\n for (const name of findDuplicates(toolNames)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['tools'],\n message: `Tool \"${name}\" is declared more than once — give every tool a unique name.`,\n });\n }\n\n const declaredTools = new Set(toolNames);\n for (const proposedName of Object.keys(definition.proposes?.tools ?? {})) {\n if (!declaredTools.has(proposedName)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['proposes', 'tools', proposedName],\n message: `proposes.tools[\"${proposedName}\"] does not match any declared tool — proposals are keyed by the exact tool name in \\`tools\\`.`,\n });\n }\n }\n });\n\n// ---- Hand-written generic types (the parts a non-generic schema cannot\n// express) plus inferred types for everything else. One home, imported by\n// both @ekanos/sdk and @kit/integrations-core. -------------------------------\n\nexport type ToolClassificationProposal = z.infer<\n typeof toolClassificationProposalSchema\n>;\nexport type IntegrationProposals = z.infer<typeof proposalsSchema>;\nexport type PartnerToolParameters = z.infer<typeof toolParametersSchema>;\nexport type IntegrationCapabilityDeclaration = z.infer<typeof capabilitySchema>;\nexport type IntegrationPermissionDeclaration = z.infer<typeof permissionSchema>;\nexport type PartnerWidgetDeclaration = z.infer<typeof widgetSchema>;\n\nexport interface PartnerToolModule<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n name: string;\n description: string;\n parameters?: PartnerToolParameters;\n /**\n * Contravariant function property (F8): the declared `Schemas` generic\n * types `ctx.storage` for the author. The generic cannot survive the\n * by-value, monomorphic host registration boundary — the adapter erases it\n * — but nothing rests on it surviving: the host constructs `ctx` FROM the\n * definition's own `storage` schemas, and `ctx.storage` validates every\n * read/write against them at runtime regardless of the handler's\n * annotation. The static generic is DX; the runtime schema is the guard.\n */\n run: (\n ctx: IntegrationContext<Schemas>,\n args: Record<string, unknown>,\n ) => Promise<unknown>;\n outputExample?: Record<string, JsonValue>;\n}\n\nexport interface IntegrationComponentDeclarations {\n activationForm?: ComponentReference;\n marketplaceTile?: ComponentReference;\n widgets?: PartnerWidgetDeclaration[];\n}\n\n// ---- Event-surface types (webhooks, schedules, OAuth) ----------------------\n//\n// Hand-written generics like `PartnerToolModule`: the `Schemas` generic types\n// `ctx.storage` for the author and is erased at the host boundary, where the\n// runtime storage validator — built from the definition's own `storage`\n// schemas — is the guard.\n\n/**\n * How the transport verifies a webhook delivery. `'none'` states explicitly\n * that the source is unsigned; otherwise the transport reads the named header\n * and verifies it against the named secret. Verification is the TRANSPORT's\n * job (the local harness logs it as skipped; the host ingress enforces it) —\n * never the partner handler's.\n */\nexport type WebhookSignatureDeclaration =\n 'none' | { header: string; secretName: string };\n\n/**\n * One delivery, as the handler receives it: transport-assigned id and receipt\n * time, the delivery headers, and the payload ALREADY parsed and validated\n * against the declaration's `payloadSchema`. A payload that fails the schema\n * never reaches the handler.\n */\nexport interface WebhookEvent {\n id: string;\n /** ISO-8601 — when the transport accepted the delivery. */\n receivedAt: string;\n headers: Record<string, string>;\n /** The parsed, schema-validated payload (output of `payloadSchema`). */\n payload: unknown;\n}\n\n/**\n * What the handler tells the transport. `processed` acknowledges the event;\n * `ignored` acknowledges it as irrelevant (still a 2xx — the sender must not\n * retry). A handler that cannot process a valid event THROWS, which the\n * transport maps to a retryable failure.\n */\nexport interface WebhookResult {\n status: 'processed' | 'ignored';\n detail?: string;\n}\n\nexport interface PartnerWebhookDeclaration<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n id: string;\n description: string;\n /** Validates every delivery before the handler runs. */\n payloadSchema: z.ZodType;\n signature: WebhookSignatureDeclaration;\n /**\n * A representative payload, JSON-compatible. Seeds the harness's payload\n * editor and documents the shape beside the schema.\n */\n examplePayload?: Record<string, JsonValue>;\n handler: (\n ctx: IntegrationContext<Schemas>,\n event: WebhookEvent,\n ) => Promise<WebhookResult>;\n}\n\n/**\n * One firing, as the handler receives it. `trigger` distinguishes the real\n * scheduler from a human pressing \"Run now\" (harness or admin) — handlers may\n * branch on it (e.g. skip idempotency windows for manual runs) but must be\n * safe under both.\n */\nexport interface ScheduleInvocation {\n /** ISO-8601 — the tick this invocation stands for. */\n scheduledFor: string;\n /** ISO-8601 — when the handler actually started. */\n invokedAt: string;\n trigger: 'schedule' | 'manual';\n}\n\n/**\n * `completed` means the run did its work; `skipped` means it correctly did\n * nothing (not configured, nothing to do). A handler that fails THROWS, which\n * the transport records as a failed run.\n */\nexport interface ScheduleResult {\n status: 'completed' | 'skipped';\n detail?: string;\n}\n\nexport interface PartnerScheduleDeclaration<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n id: string;\n description: string;\n /**\n * Standard 5-field cron (minute hour day-of-month month day-of-week),\n * validated by `defineIntegration()`. The schema package checks presence\n * only — full syntax validation is the SDK layer's job.\n */\n cron: string;\n handler: (\n ctx: IntegrationContext<Schemas>,\n invocation: ScheduleInvocation,\n ) => Promise<ScheduleResult>;\n}\n\n/**\n * The token set the transport hands `onTokens` after a code exchange (and\n * after each refresh). `raw` carries provider-specific extras verbatim.\n */\nexport interface OAuthTokens {\n accessToken: string;\n refreshToken?: string;\n /** ISO-8601 expiry, when the provider reports one. */\n expiresAt?: string;\n scope?: string;\n tokenType?: string;\n raw?: Record<string, JsonValue>;\n}\n\nexport interface OAuthProviderDeclaration {\n authorizationUrl: string;\n tokenUrl: string;\n scopes: string[];\n pkce?: boolean;\n}\n\n/**\n * The OAuth contract. The TRANSPORT owns the flow (authorize redirect, state,\n * callback, code exchange — localhost in the harness, hosted later); the\n * partner declares the provider endpoints, names the client-credential\n * secrets, and persists tokens in `onTokens` via `ctx.secrets` — so token\n * storage policy is the existing capability layer, nothing new.\n * `defineIntegration()` rejects the declaration unless both endpoint origins\n * are covered by the definition's `egress` list.\n */\nexport interface PartnerOAuthDeclaration<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n provider: OAuthProviderDeclaration;\n credentials: {\n clientIdSecretName: string;\n clientSecretSecretName: string;\n };\n onTokens: (\n ctx: IntegrationContext<Schemas>,\n tokens: OAuthTokens,\n ) => Promise<void>;\n}\n\n/**\n * The v1 activation lifecycle hook. Runs server-side with the full capability\n * ctx (egress/storage/secrets enforcement identical to `schedules[].handler`)\n * at two points:\n *\n * (a) once, after an activation is first PERSISTED — the moment a\n * cache-backed dashboard would otherwise stay empty until the next\n * schedule tick (up to a full interval);\n * (b) again, every time activationData is UPDATED — so a changed credential\n * or setting doesn't leave a stale cache in place until the next tick.\n *\n * **v1 errors are NON-FATAL.** A throw is logged and surfaced to the user as\n * a warning; the activation stays active. This hook exists for CACHE SEEDING\n * and EAGER VALIDATION (warm the `clientReadable` storage a widget reads,\n * sanity-check a credential up front) — it is explicitly NOT a connect gate.\n * A handler that must be able to REJECT the connection (fail-the-connect\n * credential validation) needs a different, future field with fatal\n * semantics; `onActivate` is not it, and must not be repurposed as one.\n *\n * Pairs with a fingerprint-guarded cache (see the SDK README's \"Activation\"\n * section): a cache invalidated only by age still serves the PREVIOUS\n * activation's data for a window even with this hook wired up — the\n * fingerprint pattern is what closes that window, `onActivate` only\n * shortens it.\n */\nexport type OnActivateHandler<Schemas extends StorageSchemas = StorageSchemas> =\n (ctx: IntegrationContext<Schemas>) => Promise<void>;\n\nexport interface IntegrationDefinition<\n Schemas extends StorageSchemas = StorageSchemas,\n> {\n slug: string;\n name: string;\n description: string;\n version: string;\n capabilities?: IntegrationCapabilityDeclaration[];\n permissions?: IntegrationPermissionDeclaration[];\n components?: IntegrationComponentDeclarations;\n workspaceTargets?: WorkspaceTargetDefinition[];\n tools?: PartnerToolModule<Schemas>[];\n storage?: Schemas;\n egress?: string[];\n webhooks?: PartnerWebhookDeclaration<Schemas>[];\n schedules?: PartnerScheduleDeclaration<Schemas>[];\n onActivate?: OnActivateHandler<Schemas>;\n oauth?: PartnerOAuthDeclaration<Schemas>;\n proposes?: IntegrationProposals;\n}\n\n// ---- Shared helpers --------------------------------------------------------\n\nfunction findDuplicates(values: readonly string[]): string[] {\n const seen = new Set<string>();\n const duplicates = new Set<string>();\n for (const value of values) {\n if (seen.has(value)) duplicates.add(value);\n seen.add(value);\n }\n return [...duplicates];\n}\n\nconst HOST_ASSIGNED_REMINDER =\n 'Host-assigned fields are never partner-authorable: productId, kind, trust ' +\n 'tier, and machine exposure (credentialModel) do not exist on ' +\n 'IntegrationDefinition, per-tool effect/sensitivity belong in the ' +\n '`proposes` block, and host-resolved widget fields (productId, ' +\n 'widgetConfigId, workspaceId, collapsed, isPinned, health, ' +\n 'integrationMetadata) are populated by the platform at runtime — remove ' +\n 'them from the definition.';\n\n// ---- Structured findings (non-throwing validation surface) -----------------\n\n/**\n * One structured validation result. This is the committed shape the CLI and\n * any other tooling consumes — the collectors below return arrays of these,\n * and the throwing entry points (`parseIntegrationDefinition`,\n * `validateIntegrationDefinitions`) are built on the exact same rules so there\n * is one rule set, not two.\n *\n * `line` is deliberately optional and usually ABSENT: zod reports a `path`\n * (`components.widgets[2].id`), not a byte offset into a source file, and\n * fabricating a line number would be a lie. The zod path lives in `message`;\n * `file` names the module the definition was loaded from when the caller knows\n * it. `hint` is always a non-empty, imperative remediation instruction.\n */\nexport interface Finding {\n check: string;\n severity: 'error' | 'warn' | 'info';\n file?: string;\n line?: number;\n message: string;\n hint: string;\n}\n\nexport interface CollectDefinitionOptions {\n /** The module the definition was loaded from, stamped onto every finding. */\n file?: string;\n}\n\nconst PLAIN_DATA_HINT =\n 'Declarations must be finite plain data. Remove the cycle, getter/setter, ' +\n 'or class/exotic instance the message names so the value cannot change ' +\n 'after validation, then re-run validate.';\n\n/** Per-issue remediation. Unrecognized keys get the host-assigned reminder. */\nfunction hintForIssue(issue: z.ZodIssue): string {\n if (issue.code === z.ZodIssueCode.unrecognized_keys) {\n return HOST_ASSIGNED_REMINDER;\n }\n const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return (\n `Correct the value at \"${path}\" so it satisfies ` +\n `@ekanos/integration-schema's IntegrationDefinitionSchema, then re-run ` +\n `validate.`\n );\n}\n\n/**\n * The single, shared implementation behind BOTH the non-throwing collector and\n * the throwing `parseIntegrationDefinition`. Runs the plain-data structural\n * check first (short-circuiting exactly as the throwing path always has), then\n * the canonical zod parse. Returns the findings, the sanitized `data` on\n * success, and whether an unrecognized key was among the failures (the\n * throwing path appends the host-assigned reminder only in that case).\n */\nfunction collectDefinitionResult(\n input: unknown,\n options: CollectDefinitionOptions = {},\n): {\n findings: Finding[];\n data?: IntegrationDefinition;\n hasUnrecognizedKey: boolean;\n} {\n const { file } = options;\n\n // Structural pre-check: the same rule assertPlainDeclaration throws on, but\n // captured as a finding. If it fails we stop here, matching the throwing\n // path which never reaches safeParse once the pre-check throws.\n try {\n assertPlainDeclaration(input);\n } catch (error) {\n return {\n findings: [\n {\n check: 'definition.plain-data',\n severity: 'error',\n ...(file ? { file } : {}),\n message: error instanceof Error ? error.message : String(error),\n hint: PLAIN_DATA_HINT,\n },\n ],\n hasUnrecognizedKey: false,\n };\n }\n\n const result = IntegrationDefinitionSchema.safeParse(input);\n if (result.success) {\n deepFreezeDefinition(result.data);\n return {\n findings: [],\n data: result.data as unknown as IntegrationDefinition,\n hasUnrecognizedKey: false,\n };\n }\n\n const hasUnrecognizedKey = result.error.issues.some(\n (issue) => issue.code === z.ZodIssueCode.unrecognized_keys,\n );\n\n const findings = result.error.issues.map((issue): Finding => {\n const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return {\n check: 'definition.schema',\n severity: 'error',\n ...(file ? { file } : {}),\n // The zod path, not a source line — see the Finding doc comment.\n message: `${path}: ${issue.message}`,\n hint: hintForIssue(issue),\n };\n });\n\n return { findings, hasUnrecognizedKey };\n}\n\n/**\n * Non-throwing sibling of `parseIntegrationDefinition`: validates a single\n * integration definition against the canonical schema and returns structured\n * findings instead of throwing a pre-formatted string. An empty array means\n * the definition is valid. Used by `@ekanos/cli validate`.\n */\nexport function collectDefinitionFindings(\n input: unknown,\n options: CollectDefinitionOptions = {},\n): Finding[] {\n return collectDefinitionResult(input, options).findings;\n}\n\n/**\n * A zod schema (storage leaf) or a React exotic (`memo`/`forwardRef`, an\n * object tagged with `$$typeof`). NOTHING here reads a property value:\n * `instanceof` walks the prototype chain and `in` is [[HasProperty]], so a\n * malicious getter still cannot execute during leaf detection (F4).\n *\n * `instanceof z.ZodType` alone is NOT sufficient. It answers \"is this an\n * instance of THIS package's zod copy\", and a partner's `zod` is routinely a\n * different copy — any transitive dependency pinning a different range, or a\n * package manager that nests instead of deduping, is enough. When the copies\n * differ, `instanceof` is false, `assertPlainDeclaration` walks INTO the\n * schema, and every storage key is rejected as \"a class/exotic instance, not\n * a plain object\" — an error that says nothing about the real cause and sends\n * the author looking at an object literal that is already correct.\n *\n * So structural detection is the fallback, matching the duck-typing\n * `isZodSchemaLike` already does one layer down for exactly this reason.\n * `~standard` is the Standard Schema marker (zod >= 3.24); `_def` +\n * `safeParse` covers older copies. This widens nothing security-relevant: a\n * value that clears this check still has to satisfy `zodSchemaRef` /\n * `storageKeyDeclarationRef`, which call `.safeParse` regardless, and the\n * re-parse at the host trust boundary remains the real guard.\n */\nfunction isDeclarationLeaf(value: object): boolean {\n if (value instanceof z.ZodType) return true;\n if ('$$typeof' in value) return true;\n\n return '~standard' in value || ('_def' in value && 'safeParse' in value);\n}\n\n/**\n * F4: reject accessor/proxy/class-instance/cyclic declaration containers\n * before parsing. An object with getters (or a proxy) can return validated\n * values during parse and different values later; a non-plain prototype can\n * smuggle mutable state past `z.object()`; a cycle would recurse into zod\n * rather than fail cleanly. We walk every CONTAINER (plain object / array),\n * and stop at legitimate leaves: functions (component refs, `run`) and zod\n * schemas (storage). Leaf detection happens BEFORE any own-property read, so\n * a `safeParse` getter cannot execute. Proxy detection is best-effort — the\n * re-parse at the host boundary (which materializes fresh values via zod) is\n * the real guard.\n *\n * Cycle detection tracks the ANCESTOR chain only (add on enter, remove on\n * exit): a genuine back-edge is a cycle, but the same object referenced from\n * two sibling branches (a DAG — e.g. a shallow-cloned widget sharing a\n * `layouts` object) is not, and must not be rejected.\n */\nexport function assertPlainDeclaration(\n value: unknown,\n path = '(root)',\n ancestors: WeakSet<object> = new WeakSet(),\n): void {\n if (value === null || typeof value !== 'object') return;\n\n if (typeof value === 'function') return;\n\n // Leaf detection first — instanceof / HasProperty never invoke a getter.\n if (isDeclarationLeaf(value)) return;\n\n if (ancestors.has(value)) {\n throw new Error(\n `Integration definition contains a cycle at ${path}. ` +\n `Declarations must be finite plain data — remove the self-reference.`,\n );\n }\n\n const proto = Object.getPrototypeOf(value) as unknown;\n const isArray = Array.isArray(value);\n if (!isArray && proto !== Object.prototype && proto !== null) {\n throw new Error(\n `Integration definition value at ${path} is a class/exotic instance, not a plain object. ` +\n `Declaration containers must be plain object/array literals so their values cannot mutate after validation.`,\n );\n }\n\n ancestors.add(value);\n for (const key of Object.keys(value)) {\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (descriptor && (descriptor.get || descriptor.set)) {\n throw new Error(\n `Integration definition property at ${path}.${key} is a getter/setter, not a data property. ` +\n `Declaration values must be plain data — a getter can return a different value after validation.`,\n );\n }\n assertPlainDeclaration(\n (value as Record<string, unknown>)[key],\n `${path}.${key}`,\n ancestors,\n );\n }\n ancestors.delete(value);\n}\n\nconst frozen = new WeakSet<object>();\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') return false;\n const proto = Object.getPrototypeOf(value) as unknown;\n return proto === Object.prototype || proto === null;\n}\n\n/**\n * Cycle-aware deep freeze (F4). Freezes plain objects and arrays; leaves zod\n * schemas (freezing breaks their internal caches) and functions alone.\n */\nexport function deepFreezeDefinition(value: unknown): void {\n if (value === null || typeof value !== 'object') return;\n if (frozen.has(value)) return;\n\n if (Array.isArray(value)) {\n frozen.add(value);\n Object.freeze(value);\n for (const item of value) deepFreezeDefinition(item);\n return;\n }\n if (isPlainObject(value)) {\n frozen.add(value);\n Object.freeze(value);\n for (const item of Object.values(value)) deepFreezeDefinition(item);\n }\n}\n\n/**\n * The single validation entry point used by BOTH `defineIntegration()` (SDK,\n * authoring time) and `registerPartnerIntegration()` (core, host trust\n * boundary — F3). Rejects non-plain containers, then parses against the\n * canonical schema, and returns the SANITIZED `result.data` (fresh, plain,\n * strict-stripped — never the caller's original object). Throws an Error\n * whose message is a remediation instruction.\n */\nexport function parseIntegrationDefinition(\n input: unknown,\n): IntegrationDefinition {\n // Built ON TOP of the collector so there is exactly one rule set. The\n // collector runs assertPlainDeclaration first (short-circuiting), then the\n // canonical parse, and freezes `data` on success (F4) — so a successful\n // result here is already the deep-frozen, sanitized definition.\n const { findings, data, hasUnrecognizedKey } = collectDefinitionResult(input);\n\n if (data) {\n // The schema's inferred output matches IntegrationDefinition in every\n // non-generic field; the storage/tool generics default to the permissive\n // base, which is exactly right for the loose registration boundary.\n return data;\n }\n\n // A plain-data structural failure is thrown verbatim (its message is already\n // a remediation: \"…contains a cycle…\", \"…is a getter/setter…\").\n const plainDataFinding = findings.find(\n (finding) => finding.check === 'definition.plain-data',\n );\n if (plainDataFinding) {\n throw new Error(plainDataFinding.message);\n }\n\n // Schema failures reproduce the historical message shape exactly: each\n // finding's message is already `${path}: ${issue.message}`, so re-prefixing\n // with ` - ` reconstructs formatIssues() verbatim.\n const slug =\n typeof (input as { slug?: unknown } | null)?.slug === 'string'\n ? ` for \"${(input as { slug: string }).slug}\"`\n : '';\n // The reminder is six lines about fields the author may not have written.\n // Appending it to EVERY failure buries the one line that matters — a bad\n // semver or a malformed cron arrives under a paragraph about productId and\n // trust tiers. Show it only when an unrecognized key is what failed, which\n // is the case it was written for.\n throw new Error(\n `Invalid integration definition${slug}:\\n` +\n findings.map((finding) => ` - ${finding.message}`).join('\\n') +\n (hasUnrecognizedKey ? `\\n${HOST_ASSIGNED_REMINDER}` : ''),\n );\n}\n\n// ---- Cross-definition collision detection (F5) -----------------------------\n\n/**\n * THE canonical effective MCP tool name — the single source of truth for how\n * discovery keys a tool. Tool discovery namespaces each raw tool name with the\n * integration slug (`slug.replace(/-/g,'_')`), skipping the prefix when the\n * name already carries it. Two collision-free RAW pairs can therefore collapse\n * to the same EFFECTIVE name (`{slug:\"foo\",tool:\"bar_baz\"}` and\n * `{slug:\"foo-bar\",tool:\"baz\"}` both become `foo_bar_baz`), so collision\n * checking MUST compare effective names, and runtime discovery MUST throw on a\n * duplicate assignment. Host-side tool discovery calls this same helper, so\n * there is one definition of the effective name.\n */\nexport function getDiscoveredToolName(slug: string, rawName: string): string {\n const slugPrefix = slug.replace(/-/g, '_');\n return rawName.startsWith(slugPrefix) ? rawName : `${slugPrefix}_${rawName}`;\n}\n\n/**\n * The metadata-only shape collision checking needs — no handlers, no schemas\n * (F8: collision validation must not force widening the tool handlers). A\n * full `IntegrationDefinition` is assignable to it.\n */\nexport interface DefinitionCollisionInput {\n slug: string;\n components?: { widgets?: readonly { id: string }[] } | null;\n tools?: readonly { name: string }[] | null;\n}\n\n/**\n * First-party identifiers a partner definition must not collide with (F5).\n * Widget lookup matches on `widget.id` and the assistant's tool list is a\n * flat name-keyed map where a later registration overwrites an earlier one,\n * so a partner reusing a first-party id/name silently hijacks it. Sourced\n * from the reviewed registry inventory, not from executing partner handlers.\n */\nexport interface FirstPartyInventory {\n slugs?: readonly string[];\n widgetIds?: readonly string[];\n /**\n * EFFECTIVE (discovery) tool names — `getDiscoveredToolName(slug, rawName)` —\n * NOT raw names, since discovery keys the flat registry by the effective name.\n */\n toolNames?: readonly string[];\n}\n\n/**\n * Detects cross-definition collisions (duplicate slugs, widget ids, tool\n * names across the partner set) AND collisions against the first-party\n * inventory, throwing one error listing EVERY collision.\n *\n * This is the build-time gate the host registry deliberately lacks:\n * `integrationRegistry.register()` keys by slug via `Map.set` and silently\n * OVERWRITES, and duplicate widget/tool ids resolve last- or\n * first-registration-wins by import order.\n */\n/**\n * One collision, split into the human `message` (unchanged from the strings\n * this module has always produced — callers and tests match on fragments like\n * `effective tool name \"foo_bar_baz\"`) and a separate imperative `hint`.\n */\ninterface CollisionRecord {\n check: string;\n message: string;\n hint: string;\n}\n\n/**\n * THE shared collision rule set, returning structured records. Both the\n * throwing `validateIntegrationDefinitions` and the non-throwing\n * `collectCollisionFindings` are built on this, so there is one rule set.\n */\nfunction computeCollisionRecords(\n definitions: readonly DefinitionCollisionInput[],\n firstParty: FirstPartyInventory = {},\n): CollisionRecord[] {\n const slugCounts = new Map<string, number>();\n const widgetOwners = new Map<string, string[]>();\n const toolOwners = new Map<string, string[]>();\n\n for (const definition of definitions) {\n slugCounts.set(definition.slug, (slugCounts.get(definition.slug) ?? 0) + 1);\n for (const widget of definition.components?.widgets ?? []) {\n widgetOwners.set(widget.id, [\n ...(widgetOwners.get(widget.id) ?? []),\n definition.slug,\n ]);\n }\n for (const tool of definition.tools ?? []) {\n // EFFECTIVE (discovery) name, not the raw name — two collision-free raw\n // names can collapse to the same effective key.\n const effective = getDiscoveredToolName(definition.slug, tool.name);\n toolOwners.set(effective, [\n ...(toolOwners.get(effective) ?? []),\n definition.slug,\n ]);\n }\n }\n\n const records: CollisionRecord[] = [];\n\n for (const [slug, count] of slugCounts) {\n if (count > 1) {\n records.push({\n check: 'collision.slug',\n message: `slug \"${slug}\" is declared by ${count} partner definitions — slugs are the registry key and must be globally unique.`,\n hint: `Rename all but one of the definitions declaring slug \"${slug}\" so every slug is globally unique.`,\n });\n }\n }\n for (const [id, owners] of widgetOwners) {\n if (owners.length > 1) {\n records.push({\n check: 'collision.widget-id',\n message: `widget id \"${id}\" is declared by [${owners.join(', ')}] — widget ids are global (widget_config rows key on them); prefix yours with the integration slug.`,\n hint: `Prefix widget id \"${id}\" with your integration slug so it is globally unique.`,\n });\n }\n }\n for (const [name, owners] of toolOwners) {\n if (owners.length > 1) {\n records.push({\n check: 'collision.tool-name',\n message: `effective tool name \"${name}\" is declared by [${owners.join(', ')}] — discovery namespaces tool names by slug, so these collapse to one flat key and overwrite each other. Rename so the slug-prefixed names differ.`,\n hint: `Rename the colliding tools so their slug-prefixed effective names differ from \"${name}\".`,\n });\n }\n }\n\n const reservedSlugs = new Set(firstParty.slugs ?? []);\n const reservedWidgets = new Set(firstParty.widgetIds ?? []);\n const reservedTools = new Set(firstParty.toolNames ?? []);\n\n for (const [slug, owners] of groupOwners(definitions, (d) => [d.slug])) {\n if (reservedSlugs.has(slug)) {\n records.push({\n check: 'collision.reserved-slug',\n message: `slug \"${slug}\" (declared by [${owners.join(', ')}]) collides with a first-party integration — pick a slug no built-in product uses.`,\n hint: `Choose a different slug than \"${slug}\" — it is reserved by a first-party integration.`,\n });\n }\n }\n for (const [id, owners] of widgetOwners) {\n if (reservedWidgets.has(id)) {\n records.push({\n check: 'collision.reserved-widget-id',\n message: `widget id \"${id}\" (declared by [${owners.join(', ')}]) collides with a first-party widget — the dashboard resolves widgets by id, so this would hijack it. Prefix with the integration slug.`,\n hint: `Prefix widget id \"${id}\" with your integration slug — it is reserved by a first-party widget.`,\n });\n }\n }\n for (const [name, owners] of toolOwners) {\n if (reservedTools.has(name)) {\n records.push({\n check: 'collision.reserved-tool-name',\n message: `effective tool name \"${name}\" (declared by [${owners.join(', ')}]) collides with a first-party tool — the flat, slug-namespaced tool registry would overwrite one with the other. Rename it.`,\n hint: `Rename the tool so its effective name is not \"${name}\" — that name is reserved by a first-party tool.`,\n });\n }\n }\n\n return records;\n}\n\n/**\n * Non-throwing sibling of `validateIntegrationDefinitions`: returns structured\n * collision findings across the partner set (and against the first-party\n * inventory) instead of throwing. An empty array means no collisions. Used by\n * `@ekanos/cli validate`.\n */\nexport function collectCollisionFindings(\n definitions: readonly DefinitionCollisionInput[],\n firstParty: FirstPartyInventory = {},\n): Finding[] {\n return computeCollisionRecords(definitions, firstParty).map(\n (record): Finding => ({\n check: record.check,\n severity: 'error',\n message: record.message,\n hint: record.hint,\n }),\n );\n}\n\nexport function validateIntegrationDefinitions(\n definitions: readonly DefinitionCollisionInput[],\n firstParty: FirstPartyInventory = {},\n): void {\n const collisions = computeCollisionRecords(definitions, firstParty).map(\n (record) => record.message,\n );\n\n if (collisions.length > 0) {\n throw new Error(\n `Integration definitions collide (${collisions.length} collision${collisions.length === 1 ? '' : 's'}):\\n` +\n collisions.map((line) => ` - ${line}`).join('\\n') +\n '\\nRename until every slug, widget id, and tool name is unique — the host registry would otherwise silently overwrite or drop a registration.',\n );\n }\n}\n\nfunction groupOwners(\n definitions: readonly DefinitionCollisionInput[],\n keysOf: (definition: DefinitionCollisionInput) => string[],\n): Map<string, string[]> {\n const owners = new Map<string, string[]>();\n for (const definition of definitions) {\n for (const key of keysOf(definition)) {\n owners.set(key, [...(owners.get(key) ?? []), definition.slug]);\n }\n }\n return owners;\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  * Deterministic identifiers derived from an integration's slug.
3
3
  *
4
4
  * The problem this solves: every hand-written seed in
5
- * `apps/web/supabase/seeds/` invents a `products.id` by hand — hex-word puns
5
+ * The database seeds invent a `products.id` by hand — hex-word puns
6
6
  * (`0d80b007` for dropbox, `1ea7b007` for learnworlds) and random v4s — and
7
7
  * nothing checks them for collisions or reproduces them anywhere else. A slug
8
8
  * therefore maps to a DIFFERENT uuid in every environment it was seeded into
@@ -2,7 +2,7 @@
2
2
  * Deterministic identifiers derived from an integration's slug.
3
3
  *
4
4
  * The problem this solves: every hand-written seed in
5
- * `apps/web/supabase/seeds/` invents a `products.id` by hand — hex-word puns
5
+ * The database seeds invent a `products.id` by hand — hex-word puns
6
6
  * (`0d80b007` for dropbox, `1ea7b007` for learnworlds) and random v4s — and
7
7
  * nothing checks them for collisions or reproduces them anywhere else. A slug
8
8
  * therefore maps to a DIFFERENT uuid in every environment it was seeded into
@@ -1 +1 @@
1
- {"version":3,"file":"product-id.js","sourceRoot":"","sources":["../src/product-id.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Deterministic identifiers derived from an integration's slug.\n *\n * The problem this solves: every hand-written seed in\n * `apps/web/supabase/seeds/` invents a `products.id` by hand — hex-word puns\n * (`0d80b007` for dropbox, `1ea7b007` for learnworlds) and random v4s — and\n * nothing checks them for collisions or reproduces them anywhere else. A slug\n * therefore maps to a DIFFERENT uuid in every environment it was seeded into\n * by hand, and re-deriving one after the fact is impossible.\n *\n * Here a slug maps to the same uuid everywhere, forever, computed rather than\n * chosen: RFC 4122 version 5 (SHA-1, name-based) under one fixed Ekanos\n * namespace. The SDK, the seed generator, and any future gate all call this\n * one function and agree by construction.\n *\n * Dependency-pure and isomorphic on purpose. This package is imported by\n * `@ekanos/sdk` and `@kit/integrations-core`, both of which reach the browser\n * bundle, so SHA-1 is implemented here in ~50 lines rather than pulled from\n * `node:crypto` (server-only) or a dependency (this package depends on zod and\n * nothing else).\n */\n\n/**\n * The standard RFC 4122 DNS namespace. Present only so\n * {@link EKANOS_INTEGRATION_NAMESPACE} below is auditable rather than\n * arbitrary — nothing else should derive ids under it.\n */\nexport const DNS_NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';\n\n/**\n * THE Ekanos integration namespace. Every id this module derives descends\n * from it, so it is a permanent constant: changing it re-points every slug at\n * a different uuid and orphans every row already written.\n *\n * It is not a hand-picked random value either — it is itself\n * `uuidv5('ekanos.dev', DNS)`, which `__tests__/product-id.test.ts` asserts,\n * so a reader can verify where it came from instead of trusting it.\n */\nexport const EKANOS_INTEGRATION_NAMESPACE =\n '269944fc-4736-5497-b751-381be92fb8df';\n\n/**\n * The slug shape `IntegrationDefinitionSchema` accepts. Re-stated (not\n * imported) so this module stays a leaf: derivation must reject anything the\n * definition schema would, because `deriveProductId('Acme')` and\n * `deriveProductId('acme')` are different uuids and only one of them is the\n * product.\n */\nconst SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nfunction assertSlug(slug: string, what: string): void {\n if (!SLUG.test(slug)) {\n throw new Error(\n `Cannot derive a ${what} from \"${slug}\": slugs are kebab-case ` +\n `([a-z0-9] segments separated by single hyphens), e.g. \"acme-crm\". ` +\n `Derivation is case- and punctuation-sensitive, so a slug that differs ` +\n `only in case would silently become a different product.`,\n );\n }\n}\n\n/**\n * The uuid `products.id` MUST carry for this integration.\n *\n * Derived as `uuidv5(slug, EKANOS_INTEGRATION_NAMESPACE)` — the name is the\n * bare slug, with no prefix, so the derivation is trivially reproducible by\n * any uuid tool (`uuidgen`, python's `uuid.uuid5`) against the namespace above.\n */\nexport function deriveProductId(slug: string): string {\n assertSlug(slug, 'product id');\n return uuidv5(slug, EKANOS_INTEGRATION_NAMESPACE);\n}\n\n/**\n * The uuid a named `plans` row for this integration MUST carry.\n *\n * Derived from `plan:<slug>:<planName>` under the same namespace. The prefix\n * keeps the plan-id space disjoint from the product-id space: a bare slug is\n * always the product, so no plan can ever collide with a product id.\n *\n * `planName` is lowercased before hashing, because `plans.name` is free text\n * ('Default') while the id must not depend on how it was capitalised.\n */\nexport function derivePlanId(slug: string, planName: string): string {\n assertSlug(slug, 'plan id');\n const normalized = planName.trim().toLowerCase();\n if (normalized.length === 0) {\n throw new Error(\n `Cannot derive a plan id for \"${slug}\": planName must be a non-empty string (e.g. \"Default\").`,\n );\n }\n return uuidv5(`plan:${slug}:${normalized}`, EKANOS_INTEGRATION_NAMESPACE);\n}\n\n// ---- RFC 4122 v5 ------------------------------------------------------------\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * RFC 4122 §4.3 version 5: SHA-1 over (namespace bytes ‖ UTF-8 name bytes),\n * with the version and variant bits overwritten in the first 16 bytes.\n *\n * Exported so tests can check it against the RFC's published vectors — the\n * only way to be sure the hand-rolled SHA-1 below is the real one.\n */\nexport function uuidv5(name: string, namespace: string): string {\n if (!UUID.test(namespace)) {\n throw new Error(\n `uuidv5 namespace \"${namespace}\" is not a uuid — pass the canonical 8-4-4-4-12 hex form.`,\n );\n }\n\n const namespaceBytes = uuidToBytes(namespace);\n const nameBytes = new TextEncoder().encode(name);\n const input = new Uint8Array(namespaceBytes.length + nameBytes.length);\n input.set(namespaceBytes, 0);\n input.set(nameBytes, namespaceBytes.length);\n\n const hash = sha1(input);\n // Version 5 in the high nibble of byte 6; RFC 4122 variant (10xx) in byte 8.\n hash[6] = ((hash[6] ?? 0) & 0x0f) | 0x50;\n hash[8] = ((hash[8] ?? 0) & 0x3f) | 0x80;\n\n const hex = [...hash.slice(0, 16)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20, 32),\n ].join('-');\n}\n\nfunction uuidToBytes(uuid: string): Uint8Array {\n const hex = uuid.replace(/-/g, '');\n const bytes = new Uint8Array(16);\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\nfunction rotl(value: number, shift: number): number {\n return ((value << shift) | (value >>> (32 - shift))) >>> 0;\n}\n\n/** FIPS 180-4 SHA-1. Returns the 20-byte digest. */\nfunction sha1(message: Uint8Array): Uint8Array {\n const bitLength = message.length * 8;\n // Padded to a multiple of 64 with room for the 0x80 marker and the 8-byte\n // big-endian length: (len + 1 + 8) rounded up to the next 64.\n const padded = new Uint8Array(Math.ceil((message.length + 9) / 64) * 64);\n padded.set(message, 0);\n padded[message.length] = 0x80;\n\n const view = new DataView(padded.buffer);\n view.setUint32(\n padded.length - 8,\n Math.floor(bitLength / 0x1_0000_0000),\n false,\n );\n view.setUint32(padded.length - 4, bitLength >>> 0, false);\n\n let h0 = 0x67452301;\n let h1 = 0xefcdab89;\n let h2 = 0x98badcfe;\n let h3 = 0x10325476;\n let h4 = 0xc3d2e1f0;\n\n const w = new Array<number>(80).fill(0);\n\n for (let offset = 0; offset < padded.length; offset += 64) {\n for (let i = 0; i < 16; i += 1) {\n w[i] = view.getUint32(offset + i * 4, false);\n }\n for (let i = 16; i < 80; i += 1) {\n w[i] = rotl(\n (w[i - 3] ?? 0) ^ (w[i - 8] ?? 0) ^ (w[i - 14] ?? 0) ^ (w[i - 16] ?? 0),\n 1,\n );\n }\n\n let a = h0;\n let b = h1;\n let c = h2;\n let d = h3;\n let e = h4;\n\n for (let i = 0; i < 80; i += 1) {\n let f: number;\n let k: number;\n if (i < 20) {\n f = ((b & c) | (~b & d)) >>> 0;\n k = 0x5a827999;\n } else if (i < 40) {\n f = (b ^ c ^ d) >>> 0;\n k = 0x6ed9eba1;\n } else if (i < 60) {\n f = ((b & c) | (b & d) | (c & d)) >>> 0;\n k = 0x8f1bbcdc;\n } else {\n f = (b ^ c ^ d) >>> 0;\n k = 0xca62c1d6;\n }\n\n // Every term is < 2^32, so the five-way sum stays well inside the\n // 2^53 exact-integer range; >>> 0 folds it back to uint32.\n const temp = (rotl(a, 5) + f + e + k + (w[i] ?? 0)) >>> 0;\n e = d;\n d = c;\n c = rotl(b, 30);\n b = a;\n a = temp;\n }\n\n h0 = (h0 + a) >>> 0;\n h1 = (h1 + b) >>> 0;\n h2 = (h2 + c) >>> 0;\n h3 = (h3 + d) >>> 0;\n h4 = (h4 + e) >>> 0;\n }\n\n const digest = new Uint8Array(20);\n const digestView = new DataView(digest.buffer);\n digestView.setUint32(0, h0, false);\n digestView.setUint32(4, h1, false);\n digestView.setUint32(8, h2, false);\n digestView.setUint32(12, h3, false);\n digestView.setUint32(16, h4, false);\n return digest;\n}\n"]}
1
+ {"version":3,"file":"product-id.js","sourceRoot":"","sources":["../src/product-id.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Deterministic identifiers derived from an integration's slug.\n *\n * The problem this solves: every hand-written seed in\n * The database seeds invent a `products.id` by hand — hex-word puns\n * (`0d80b007` for dropbox, `1ea7b007` for learnworlds) and random v4s — and\n * nothing checks them for collisions or reproduces them anywhere else. A slug\n * therefore maps to a DIFFERENT uuid in every environment it was seeded into\n * by hand, and re-deriving one after the fact is impossible.\n *\n * Here a slug maps to the same uuid everywhere, forever, computed rather than\n * chosen: RFC 4122 version 5 (SHA-1, name-based) under one fixed Ekanos\n * namespace. The SDK, the seed generator, and any future gate all call this\n * one function and agree by construction.\n *\n * Dependency-pure and isomorphic on purpose. This package is imported by\n * `@ekanos/sdk` and `@kit/integrations-core`, both of which reach the browser\n * bundle, so SHA-1 is implemented here in ~50 lines rather than pulled from\n * `node:crypto` (server-only) or a dependency (this package depends on zod and\n * nothing else).\n */\n\n/**\n * The standard RFC 4122 DNS namespace. Present only so\n * {@link EKANOS_INTEGRATION_NAMESPACE} below is auditable rather than\n * arbitrary — nothing else should derive ids under it.\n */\nexport const DNS_NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';\n\n/**\n * THE Ekanos integration namespace. Every id this module derives descends\n * from it, so it is a permanent constant: changing it re-points every slug at\n * a different uuid and orphans every row already written.\n *\n * It is not a hand-picked random value either — it is itself\n * `uuidv5('ekanos.dev', DNS)`, which `__tests__/product-id.test.ts` asserts,\n * so a reader can verify where it came from instead of trusting it.\n */\nexport const EKANOS_INTEGRATION_NAMESPACE =\n '269944fc-4736-5497-b751-381be92fb8df';\n\n/**\n * The slug shape `IntegrationDefinitionSchema` accepts. Re-stated (not\n * imported) so this module stays a leaf: derivation must reject anything the\n * definition schema would, because `deriveProductId('Acme')` and\n * `deriveProductId('acme')` are different uuids and only one of them is the\n * product.\n */\nconst SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\nfunction assertSlug(slug: string, what: string): void {\n if (!SLUG.test(slug)) {\n throw new Error(\n `Cannot derive a ${what} from \"${slug}\": slugs are kebab-case ` +\n `([a-z0-9] segments separated by single hyphens), e.g. \"acme-crm\". ` +\n `Derivation is case- and punctuation-sensitive, so a slug that differs ` +\n `only in case would silently become a different product.`,\n );\n }\n}\n\n/**\n * The uuid `products.id` MUST carry for this integration.\n *\n * Derived as `uuidv5(slug, EKANOS_INTEGRATION_NAMESPACE)` — the name is the\n * bare slug, with no prefix, so the derivation is trivially reproducible by\n * any uuid tool (`uuidgen`, python's `uuid.uuid5`) against the namespace above.\n */\nexport function deriveProductId(slug: string): string {\n assertSlug(slug, 'product id');\n return uuidv5(slug, EKANOS_INTEGRATION_NAMESPACE);\n}\n\n/**\n * The uuid a named `plans` row for this integration MUST carry.\n *\n * Derived from `plan:<slug>:<planName>` under the same namespace. The prefix\n * keeps the plan-id space disjoint from the product-id space: a bare slug is\n * always the product, so no plan can ever collide with a product id.\n *\n * `planName` is lowercased before hashing, because `plans.name` is free text\n * ('Default') while the id must not depend on how it was capitalised.\n */\nexport function derivePlanId(slug: string, planName: string): string {\n assertSlug(slug, 'plan id');\n const normalized = planName.trim().toLowerCase();\n if (normalized.length === 0) {\n throw new Error(\n `Cannot derive a plan id for \"${slug}\": planName must be a non-empty string (e.g. \"Default\").`,\n );\n }\n return uuidv5(`plan:${slug}:${normalized}`, EKANOS_INTEGRATION_NAMESPACE);\n}\n\n// ---- RFC 4122 v5 ------------------------------------------------------------\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * RFC 4122 §4.3 version 5: SHA-1 over (namespace bytes ‖ UTF-8 name bytes),\n * with the version and variant bits overwritten in the first 16 bytes.\n *\n * Exported so tests can check it against the RFC's published vectors — the\n * only way to be sure the hand-rolled SHA-1 below is the real one.\n */\nexport function uuidv5(name: string, namespace: string): string {\n if (!UUID.test(namespace)) {\n throw new Error(\n `uuidv5 namespace \"${namespace}\" is not a uuid — pass the canonical 8-4-4-4-12 hex form.`,\n );\n }\n\n const namespaceBytes = uuidToBytes(namespace);\n const nameBytes = new TextEncoder().encode(name);\n const input = new Uint8Array(namespaceBytes.length + nameBytes.length);\n input.set(namespaceBytes, 0);\n input.set(nameBytes, namespaceBytes.length);\n\n const hash = sha1(input);\n // Version 5 in the high nibble of byte 6; RFC 4122 variant (10xx) in byte 8.\n hash[6] = ((hash[6] ?? 0) & 0x0f) | 0x50;\n hash[8] = ((hash[8] ?? 0) & 0x3f) | 0x80;\n\n const hex = [...hash.slice(0, 16)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20, 32),\n ].join('-');\n}\n\nfunction uuidToBytes(uuid: string): Uint8Array {\n const hex = uuid.replace(/-/g, '');\n const bytes = new Uint8Array(16);\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\nfunction rotl(value: number, shift: number): number {\n return ((value << shift) | (value >>> (32 - shift))) >>> 0;\n}\n\n/** FIPS 180-4 SHA-1. Returns the 20-byte digest. */\nfunction sha1(message: Uint8Array): Uint8Array {\n const bitLength = message.length * 8;\n // Padded to a multiple of 64 with room for the 0x80 marker and the 8-byte\n // big-endian length: (len + 1 + 8) rounded up to the next 64.\n const padded = new Uint8Array(Math.ceil((message.length + 9) / 64) * 64);\n padded.set(message, 0);\n padded[message.length] = 0x80;\n\n const view = new DataView(padded.buffer);\n view.setUint32(\n padded.length - 8,\n Math.floor(bitLength / 0x1_0000_0000),\n false,\n );\n view.setUint32(padded.length - 4, bitLength >>> 0, false);\n\n let h0 = 0x67452301;\n let h1 = 0xefcdab89;\n let h2 = 0x98badcfe;\n let h3 = 0x10325476;\n let h4 = 0xc3d2e1f0;\n\n const w = new Array<number>(80).fill(0);\n\n for (let offset = 0; offset < padded.length; offset += 64) {\n for (let i = 0; i < 16; i += 1) {\n w[i] = view.getUint32(offset + i * 4, false);\n }\n for (let i = 16; i < 80; i += 1) {\n w[i] = rotl(\n (w[i - 3] ?? 0) ^ (w[i - 8] ?? 0) ^ (w[i - 14] ?? 0) ^ (w[i - 16] ?? 0),\n 1,\n );\n }\n\n let a = h0;\n let b = h1;\n let c = h2;\n let d = h3;\n let e = h4;\n\n for (let i = 0; i < 80; i += 1) {\n let f: number;\n let k: number;\n if (i < 20) {\n f = ((b & c) | (~b & d)) >>> 0;\n k = 0x5a827999;\n } else if (i < 40) {\n f = (b ^ c ^ d) >>> 0;\n k = 0x6ed9eba1;\n } else if (i < 60) {\n f = ((b & c) | (b & d) | (c & d)) >>> 0;\n k = 0x8f1bbcdc;\n } else {\n f = (b ^ c ^ d) >>> 0;\n k = 0xca62c1d6;\n }\n\n // Every term is < 2^32, so the five-way sum stays well inside the\n // 2^53 exact-integer range; >>> 0 folds it back to uint32.\n const temp = (rotl(a, 5) + f + e + k + (w[i] ?? 0)) >>> 0;\n e = d;\n d = c;\n c = rotl(b, 30);\n b = a;\n a = temp;\n }\n\n h0 = (h0 + a) >>> 0;\n h1 = (h1 + b) >>> 0;\n h2 = (h2 + c) >>> 0;\n h3 = (h3 + d) >>> 0;\n h4 = (h4 + e) >>> 0;\n }\n\n const digest = new Uint8Array(20);\n const digestView = new DataView(digest.buffer);\n digestView.setUint32(0, h0, false);\n digestView.setUint32(4, h1, false);\n digestView.setUint32(8, h2, false);\n digestView.setUint32(12, h3, false);\n digestView.setUint32(16, h4, false);\n return digest;\n}\n"]}
@@ -15,7 +15,7 @@
15
15
  * the target count cap — is single-sourced here.
16
16
  *
17
17
  * Ported from `@kit/integrations-core`'s original schema; field constraints
18
- * mirror the `workspaces` table (`apps/web/supabase/schemas/92-workspaces.sql`).
18
+ * mirror the host's `workspaces` table.
19
19
  */
20
20
  import { z } from 'zod';
21
21
  export interface WorkspaceTargetSchemaOptions {
@@ -15,7 +15,7 @@
15
15
  * the target count cap — is single-sourced here.
16
16
  *
17
17
  * Ported from `@kit/integrations-core`'s original schema; field constraints
18
- * mirror the `workspaces` table (`apps/web/supabase/schemas/92-workspaces.sql`).
18
+ * mirror the host's `workspaces` table.
19
19
  */
20
20
  import { z } from 'zod';
21
21
  /** Stable URL segment — `/workspace/<slug>` routes reference it, so it may
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-target.js","sourceRoot":"","sources":["../src/workspace-target.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The canonical, dependency-pure workspace-target contract (F10): ONE schema\n * for SDK authoring validation, host CI, and runtime normalization — no\n * reduced reimplementation on the SDK side.\n *\n * Icon-NAME validity is the one host-specific rule this package cannot own:\n * whether a string is a real Font Awesome / lucide name lives in the host\n * design system (`@kit/ui/icon-shared`), which is not dependency-pure. So the\n * schema validates the icon structurally (a bounded non-empty string) and\n * accepts an INJECTED `isValidIcon` predicate: the host passes its own\n * (`@kit/integrations-core`'s `workspace-target.schema.ts`), the SDK omits it\n * (a partner's icon set is the host's to judge at the promotion gate). Every\n * other rule — slug format, length bounds, the mutual-exclusivity of\n * `inheritsWidgets`/`includedWidgetIds`, unique slugs, exactly-one-inheriting,\n * the target count cap — is single-sourced here.\n *\n * Ported from `@kit/integrations-core`'s original schema; field constraints\n * mirror the `workspaces` table (`apps/web/supabase/schemas/92-workspaces.sql`).\n */\nimport { z } from 'zod';\n\nexport interface WorkspaceTargetSchemaOptions {\n /**\n * Host-injected icon-name validator. When provided, an icon that fails it\n * is rejected; when omitted (SDK path), only the structural string check\n * applies.\n */\n isValidIcon?: (icon: string) => boolean;\n}\n\n/** Stable URL segment — `/workspace/<slug>` routes reference it, so it may\n * never contain anything needing escaping. */\nexport const WorkspaceTargetSlugSchema = z\n .string()\n .trim()\n .min(1, 'Slug is required')\n .max(100)\n .regex(\n /^[a-z0-9]+(?:-[a-z0-9]+)*$/,\n 'Must be lowercase alphanumeric with single hyphens between segments',\n );\n\n/** Layouts a declaration may select. `'table'` still exists on the DB enum but\n * is dead legacy since plan 038 — no declaration may write it. */\nexport const WorkspaceTargetLayoutSchema = z.enum(['masonry', 'two-column']);\n\nexport function createWorkspaceTargetSchema(\n options: WorkspaceTargetSchemaOptions = {},\n) {\n const iconSchema = z\n .string()\n .trim()\n .min(1)\n .max(100)\n .refine((icon) => options.isValidIcon?.(icon) ?? true, {\n message: 'Must be a valid icon',\n });\n\n return z\n .object({\n slug: WorkspaceTargetSlugSchema,\n name: z.string().trim().min(1, 'Name is required').max(100),\n icon: iconSchema.optional(),\n description: z.string().trim().max(500).optional(),\n layout: WorkspaceTargetLayoutSchema.default('masonry'),\n /**\n * Whether this workspace receives the product's otherwise-unplaced\n * widgets. Exactly one target per product must set it — see the list\n * schema below.\n */\n inheritsWidgets: z.boolean().optional(),\n /** Limit this workspace to specific widget ids. */\n includedWidgetIds: z.array(z.string().trim().min(1)).max(100).optional(),\n })\n .strict()\n .superRefine((target, ctx) => {\n if (target.inheritsWidgets && target.includedWidgetIds) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['includedWidgetIds'],\n message:\n 'inheritsWidgets and includedWidgetIds are mutually exclusive — a widget-subset workspace cannot also inherit unplaced widgets',\n });\n }\n\n if (target.includedWidgetIds?.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['includedWidgetIds'],\n message:\n 'includedWidgetIds must list at least one widget id — omit the field for a workspace that inherits instead',\n });\n }\n });\n}\n\n/** Default (SDK-side) target schema: structural, no host icon-name check. */\nexport const WorkspaceTargetSchema = createWorkspaceTargetSchema();\n\nexport type WorkspaceTargetDefinition = z.input<typeof WorkspaceTargetSchema>;\n/** A target after parsing, with `layout` defaulted. */\nexport type ResolvedWorkspaceTarget = z.output<typeof WorkspaceTargetSchema>;\n\nfunction refineTargetList(\n targets: ResolvedWorkspaceTarget[],\n ctx: z.RefinementCtx,\n) {\n const seenSlugs = new Set<string>();\n\n for (const [index, target] of targets.entries()) {\n if (seenSlugs.has(target.slug)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [index, 'slug'],\n message: `Duplicate workspace slug \"${target.slug}\" — every target in one declaration set needs its own slug`,\n });\n }\n\n seenSlugs.add(target.slug);\n }\n\n if (targets.length === 0) return;\n\n const canInherit = (target: ResolvedWorkspaceTarget) =>\n !target.includedWidgetIds;\n const inheriting = targets.filter(\n (target) => canInherit(target) && target.inheritsWidgets,\n );\n\n if (inheriting.length === 1) return;\n\n // A lone non-subset target is unambiguous — `normalizeWorkspaceTargets`\n // fills the flag in for it.\n if (\n inheriting.length === 0 &&\n targets.length === 1 &&\n canInherit(targets[0]!)\n ) {\n return;\n }\n\n if (inheriting.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'No target can receive this product’s unplaced widgets — exactly one target without includedWidgetIds must set inheritsWidgets: true',\n });\n\n return;\n }\n\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${inheriting.length} targets set inheritsWidgets — exactly one must, since a product has at most one inheriting attachment per scope`,\n });\n}\n\n/**\n * A whole product's declaration set. The cross-target invariants (unique\n * slugs, exactly one inheriting target, a max count) live here because none\n * is checkable from inside a single target.\n */\nexport function createWorkspaceTargetListSchema(\n options: WorkspaceTargetSchemaOptions = {},\n) {\n return z\n .array(createWorkspaceTargetSchema(options))\n .max(20)\n .superRefine(refineTargetList);\n}\n\nexport const WorkspaceTargetListSchema = createWorkspaceTargetListSchema();\n\n/**\n * Parses a declaration set and fills in the one benign omission the list\n * schema tolerates (a single non-subset target that never set\n * `inheritsWidgets`). Returns the issues instead of throwing so callers\n * choose their own failure mode — CI fails the build, materialization logs\n * and skips.\n */\nexport function normalizeWorkspaceTargets(\n declared: unknown,\n options: WorkspaceTargetSchemaOptions = {},\n) {\n const parsed = createWorkspaceTargetListSchema(options).safeParse(declared);\n\n if (!parsed.success) {\n return { ok: false as const, error: parsed.error };\n }\n\n const targets = parsed.data;\n\n if (\n targets.length === 1 &&\n !targets[0]!.includedWidgetIds &&\n !targets[0]!.inheritsWidgets\n ) {\n return {\n ok: true as const,\n targets: [{ ...targets[0]!, inheritsWidgets: true }],\n };\n }\n\n return { ok: true as const, targets };\n}\n"]}
1
+ {"version":3,"file":"workspace-target.js","sourceRoot":"","sources":["../src/workspace-target.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The canonical, dependency-pure workspace-target contract (F10): ONE schema\n * for SDK authoring validation, host CI, and runtime normalization — no\n * reduced reimplementation on the SDK side.\n *\n * Icon-NAME validity is the one host-specific rule this package cannot own:\n * whether a string is a real Font Awesome / lucide name lives in the host\n * design system (`@kit/ui/icon-shared`), which is not dependency-pure. So the\n * schema validates the icon structurally (a bounded non-empty string) and\n * accepts an INJECTED `isValidIcon` predicate: the host passes its own\n * (`@kit/integrations-core`'s `workspace-target.schema.ts`), the SDK omits it\n * (a partner's icon set is the host's to judge at the promotion gate). Every\n * other rule — slug format, length bounds, the mutual-exclusivity of\n * `inheritsWidgets`/`includedWidgetIds`, unique slugs, exactly-one-inheriting,\n * the target count cap — is single-sourced here.\n *\n * Ported from `@kit/integrations-core`'s original schema; field constraints\n * mirror the host's `workspaces` table.\n */\nimport { z } from 'zod';\n\nexport interface WorkspaceTargetSchemaOptions {\n /**\n * Host-injected icon-name validator. When provided, an icon that fails it\n * is rejected; when omitted (SDK path), only the structural string check\n * applies.\n */\n isValidIcon?: (icon: string) => boolean;\n}\n\n/** Stable URL segment — `/workspace/<slug>` routes reference it, so it may\n * never contain anything needing escaping. */\nexport const WorkspaceTargetSlugSchema = z\n .string()\n .trim()\n .min(1, 'Slug is required')\n .max(100)\n .regex(\n /^[a-z0-9]+(?:-[a-z0-9]+)*$/,\n 'Must be lowercase alphanumeric with single hyphens between segments',\n );\n\n/** Layouts a declaration may select. `'table'` still exists on the DB enum but\n * is dead legacy since plan 038 — no declaration may write it. */\nexport const WorkspaceTargetLayoutSchema = z.enum(['masonry', 'two-column']);\n\nexport function createWorkspaceTargetSchema(\n options: WorkspaceTargetSchemaOptions = {},\n) {\n const iconSchema = z\n .string()\n .trim()\n .min(1)\n .max(100)\n .refine((icon) => options.isValidIcon?.(icon) ?? true, {\n message: 'Must be a valid icon',\n });\n\n return z\n .object({\n slug: WorkspaceTargetSlugSchema,\n name: z.string().trim().min(1, 'Name is required').max(100),\n icon: iconSchema.optional(),\n description: z.string().trim().max(500).optional(),\n layout: WorkspaceTargetLayoutSchema.default('masonry'),\n /**\n * Whether this workspace receives the product's otherwise-unplaced\n * widgets. Exactly one target per product must set it — see the list\n * schema below.\n */\n inheritsWidgets: z.boolean().optional(),\n /** Limit this workspace to specific widget ids. */\n includedWidgetIds: z.array(z.string().trim().min(1)).max(100).optional(),\n })\n .strict()\n .superRefine((target, ctx) => {\n if (target.inheritsWidgets && target.includedWidgetIds) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['includedWidgetIds'],\n message:\n 'inheritsWidgets and includedWidgetIds are mutually exclusive — a widget-subset workspace cannot also inherit unplaced widgets',\n });\n }\n\n if (target.includedWidgetIds?.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['includedWidgetIds'],\n message:\n 'includedWidgetIds must list at least one widget id — omit the field for a workspace that inherits instead',\n });\n }\n });\n}\n\n/** Default (SDK-side) target schema: structural, no host icon-name check. */\nexport const WorkspaceTargetSchema = createWorkspaceTargetSchema();\n\nexport type WorkspaceTargetDefinition = z.input<typeof WorkspaceTargetSchema>;\n/** A target after parsing, with `layout` defaulted. */\nexport type ResolvedWorkspaceTarget = z.output<typeof WorkspaceTargetSchema>;\n\nfunction refineTargetList(\n targets: ResolvedWorkspaceTarget[],\n ctx: z.RefinementCtx,\n) {\n const seenSlugs = new Set<string>();\n\n for (const [index, target] of targets.entries()) {\n if (seenSlugs.has(target.slug)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: [index, 'slug'],\n message: `Duplicate workspace slug \"${target.slug}\" — every target in one declaration set needs its own slug`,\n });\n }\n\n seenSlugs.add(target.slug);\n }\n\n if (targets.length === 0) return;\n\n const canInherit = (target: ResolvedWorkspaceTarget) =>\n !target.includedWidgetIds;\n const inheriting = targets.filter(\n (target) => canInherit(target) && target.inheritsWidgets,\n );\n\n if (inheriting.length === 1) return;\n\n // A lone non-subset target is unambiguous — `normalizeWorkspaceTargets`\n // fills the flag in for it.\n if (\n inheriting.length === 0 &&\n targets.length === 1 &&\n canInherit(targets[0]!)\n ) {\n return;\n }\n\n if (inheriting.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'No target can receive this product’s unplaced widgets — exactly one target without includedWidgetIds must set inheritsWidgets: true',\n });\n\n return;\n }\n\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${inheriting.length} targets set inheritsWidgets — exactly one must, since a product has at most one inheriting attachment per scope`,\n });\n}\n\n/**\n * A whole product's declaration set. The cross-target invariants (unique\n * slugs, exactly one inheriting target, a max count) live here because none\n * is checkable from inside a single target.\n */\nexport function createWorkspaceTargetListSchema(\n options: WorkspaceTargetSchemaOptions = {},\n) {\n return z\n .array(createWorkspaceTargetSchema(options))\n .max(20)\n .superRefine(refineTargetList);\n}\n\nexport const WorkspaceTargetListSchema = createWorkspaceTargetListSchema();\n\n/**\n * Parses a declaration set and fills in the one benign omission the list\n * schema tolerates (a single non-subset target that never set\n * `inheritsWidgets`). Returns the issues instead of throwing so callers\n * choose their own failure mode — CI fails the build, materialization logs\n * and skips.\n */\nexport function normalizeWorkspaceTargets(\n declared: unknown,\n options: WorkspaceTargetSchemaOptions = {},\n) {\n const parsed = createWorkspaceTargetListSchema(options).safeParse(declared);\n\n if (!parsed.success) {\n return { ok: false as const, error: parsed.error };\n }\n\n const targets = parsed.data;\n\n if (\n targets.length === 1 &&\n !targets[0]!.includedWidgetIds &&\n !targets[0]!.inheritsWidgets\n ) {\n return {\n ok: true as const,\n targets: [{ ...targets[0]!, inheritsWidgets: true }],\n };\n }\n\n return { ok: true as const, targets };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekanos/integration-schema",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Canonical, dependency-pure zod schema + types for Ekanos integration definitions. Depended on by @ekanos/sdk and @kit/integrations-core; depends on neither, so there is no cycle and there is exactly one schema, one inferred type.",
6
6
  "license": "MIT",
@@ -25,12 +25,12 @@
25
25
  "zod": "^3.25.76"
26
26
  },
27
27
  "devDependencies": {
28
- "typescript": "^5.9.3",
29
- "vitest": "4.1.10",
30
- "zod": "^3.25.74",
31
28
  "@kit/eslint-config": "0.2.0",
32
29
  "@kit/prettier-config": "0.1.0",
33
- "@kit/tsconfig": "0.1.0"
30
+ "@kit/tsconfig": "0.1.0",
31
+ "typescript": "^5.9.3",
32
+ "vitest": "4.1.10",
33
+ "zod": "3.25.76"
34
34
  },
35
35
  "prettier": "@kit/prettier-config",
36
36
  "scripts": {