@ekanos/integration-schema 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vastly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # @ekanos/integration-schema
2
+
3
+ The canonical, dependency-pure contract for Ekanos integration definitions.
4
+
5
+ One zod schema and one set of types, imported by **both** `@ekanos/sdk`
6
+ (authoring — `defineIntegration()`) and `@kit/integrations-core` (the host
7
+ trust boundary — `registerPartnerIntegration()` re-parses against the same
8
+ schema). This package depends on nothing but `zod`, so neither consumer forms
9
+ a dependency cycle and there is no hand-written structural twin to drift.
10
+
11
+ ## Why it exists
12
+
13
+ Adversarial review (GPT-5.6, 2026-08-27) found three problems a shared schema
14
+ resolves at once:
15
+
16
+ - **F3** — the host must re-parse partner exports at the trust boundary, not
17
+ 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
23
+ workspace-target schema lives here; the host injects icon-name validity via
24
+ `createWorkspaceTargetListSchema({ isValidIcon })`.
25
+
26
+ ## What's here
27
+
28
+ - `IntegrationDefinitionSchema` — the strict canonical zod schema.
29
+ - `parseIntegrationDefinition(input)` — rejects non-plain containers, parses,
30
+ returns sanitized `result.data` (never the caller's object).
31
+ - `validateIntegrationDefinitions(defs, firstParty?)` — cross-definition +
32
+ first-party collision detection.
33
+ - `IntegrationDefinition<Schemas>` and friends — the generic authoring types.
34
+ - The capability-context types (`IntegrationContext`, `StorageSchemas`, …) —
35
+ canonical home; `@ekanos/sdk` re-exports them.
36
+ - The workspace-target schema + `normalizeWorkspaceTargets`.
37
+
38
+ Dependency-pure by construction: `zod` only. No `@kit/*`, no React, no
39
+ `server-only`.
40
+
41
+ ## Publishing
42
+
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
47
+ so a real Node import resolves without a bundler; the SDK's `pack:test`
48
+ exercises that path end to end.
@@ -0,0 +1,178 @@
1
+ /**
2
+ * The capability context — the one object partner code receives.
3
+ *
4
+ * Canonical home (moved here from `@ekanos/sdk`'s `context/types.ts` so the
5
+ * definition schema below can reference `IntegrationContext` without a
6
+ * package cycle; `@ekanos/sdk` re-exports every type from here). Pure types,
7
+ * zod-type-only, erased at compile time. The value half of the contract —
8
+ * error classes, the egress matcher, the storage validators — lives in
9
+ * `@ekanos/sdk/context`; the in-memory mock in `@ekanos/sdk/testing`.
10
+ * Spec: docs/devex/capability-context-proposal.md (all six §7 rulings).
11
+ */
12
+ import type { z } from 'zod';
13
+ /**
14
+ * Who is executing. A closed union: a machine context without a grant is
15
+ * unrepresentable rather than merely checked (proposal §2).
16
+ */
17
+ export type IntegrationActor = {
18
+ readonly kind: 'user';
19
+ readonly userId: string;
20
+ } | {
21
+ readonly kind: 'machine';
22
+ readonly tokenId: string;
23
+ readonly createdBy: string | null;
24
+ };
25
+ /**
26
+ * One storage key's declaration in EXPLICIT form: the zod schema plus the
27
+ * exposure flags that decide which host surfaces may serve the key.
28
+ *
29
+ * Exposure is opt-in and fails closed. A declaration's keys are server-side
30
+ * by construction — partner code reaches them only through `ctx.storage`,
31
+ * which runs behind the capability layer. The generic browser-readable
32
+ * storage route (`GET /api/integrations/[slug]/storage`) would otherwise
33
+ * turn EVERY declared key into a browser-readable one, including keys a
34
+ * partner reasonably treated as server-only: sync cursors, cached upstream
35
+ * responses, internal bookkeeping. So the route serves a key only when its
36
+ * declaration opts in with `clientReadable: true`.
37
+ */
38
+ export interface StorageKeyDeclaration<S extends z.ZodType = z.ZodType> {
39
+ readonly schema: S;
40
+ /**
41
+ * Opt in to the generic browser-readable storage route. **Defaults to
42
+ * `false`** — an omitted flag, and the bare-schema declaration form, both
43
+ * mean server-only. Set it only for values the browser legitimately needs
44
+ * and that are safe for any active member of the account to read (widget
45
+ * settings, display preferences). Never for cursors, cached upstream
46
+ * payloads, or anything a `secret` would be a better home for.
47
+ */
48
+ readonly clientReadable?: boolean;
49
+ }
50
+ /**
51
+ * How a single storage key may be declared: a bare zod schema (the original
52
+ * form — always server-only), or a `StorageKeyDeclaration` descriptor that
53
+ * carries exposure flags alongside the schema. Both forms validate reads and
54
+ * writes identically; only the descriptor can widen exposure.
55
+ */
56
+ export type StorageKeyDeclarationInput<S extends z.ZodType = z.ZodType> = S | StorageKeyDeclaration<S>;
57
+ /**
58
+ * Storage schemas for one scope: key → zod schema, or key → declaration
59
+ * descriptor (ruling 1). Keys are strict — a key with no declared schema
60
+ * cannot be read, written, or deleted; the attempt is a type error where the
61
+ * schema map is statically known, and always a runtime
62
+ * `StorageValidationError`.
63
+ */
64
+ export type StorageSchemaMap = Record<string, StorageKeyDeclarationInput>;
65
+ /**
66
+ * Resolves the zod schema out of either declaration form, so `ScopedStore`
67
+ * stays schema-typed whichever form the author chose.
68
+ */
69
+ export type StorageKeySchema<D> = [D] extends [z.ZodType] ? D : D extends StorageKeyDeclaration<infer S> ? S : D extends z.ZodType ? D : never;
70
+ /**
71
+ * The storage declaration `defineIntegration({ storage })` consumes and
72
+ * `createMockContext({ storageSchemas })` enforces (ruling 1). A scope left
73
+ * undeclared has no usable keys.
74
+ */
75
+ export interface StorageSchemas {
76
+ account?: StorageSchemaMap;
77
+ user?: StorageSchemaMap;
78
+ }
79
+ /**
80
+ * Resolves the schema map for one scope of a declaration. A scope the
81
+ * integration did not declare resolves to an empty map (no key typechecks);
82
+ * only the unparameterized default (`StorageSchemas` itself) stays
83
+ * permissive, for signatures that cannot know the integration's schemas.
84
+ */
85
+ export type StorageScopeSchemas<Schemas extends StorageSchemas, Scope extends keyof StorageSchemas> = [Schemas[Scope]] extends [undefined] ? Record<never, never> : NonNullable<Schemas[Scope]>;
86
+ export interface StorageEntry<T> {
87
+ data: T;
88
+ externalId: string | null;
89
+ expiresAt: string | null;
90
+ updatedAt: string;
91
+ }
92
+ export interface StorageWriteOptions {
93
+ externalId?: string;
94
+ /** ISO-8601 timestamp; an entry past it reads as `null`. */
95
+ expiresAt?: string;
96
+ }
97
+ /**
98
+ * One scope of `ctx.storage`, bound to `{account, product}` (and `userId`
99
+ * for the user scope) at construction — there is no id parameter to lie in.
100
+ *
101
+ * Schema-typed (ruling 1): `get` returns the declared schema's output type,
102
+ * `set` validates before write, and a read whose stored data no longer
103
+ * matches the schema throws `StorageValidationError` — schema evolution is
104
+ * handled explicitly (versioned keys or `z.union`), never silently.
105
+ * All methods are async (ruling 4).
106
+ */
107
+ export interface ScopedStore<Schemas extends StorageSchemaMap = StorageSchemaMap> {
108
+ get<K extends keyof Schemas & string>(key: K): Promise<StorageEntry<z.output<StorageKeySchema<Schemas[K]>>> | null>;
109
+ set<K extends keyof Schemas & string>(key: K, data: z.input<StorageKeySchema<Schemas[K]>>, options?: StorageWriteOptions): Promise<void>;
110
+ delete<K extends keyof Schemas & string>(key: K): Promise<void>;
111
+ }
112
+ export interface IntegrationStorage<Schemas extends StorageSchemas = StorageSchemas> {
113
+ /** account_product_data, bound to {accountId, productId}. */
114
+ readonly account: ScopedStore<StorageScopeSchemas<Schemas, 'account'>>;
115
+ /**
116
+ * user_product_data, bound to {userId, accountId, productId}.
117
+ * Throws for machine actors.
118
+ */
119
+ readonly user: ScopedStore<StorageScopeSchemas<Schemas, 'user'>>;
120
+ }
121
+ /**
122
+ * Reads from the credential set the host resolved (account → source →
123
+ * global tiers + hydrated activation secrets) before partner code ran.
124
+ * All methods are async (ruling 4).
125
+ */
126
+ export interface IntegrationSecrets {
127
+ /** A named field of the resolved credential set + hydrated activation secrets. */
128
+ get(name: string): Promise<string | null>;
129
+ /** All resolved secret names (values not included) — for capability probing. */
130
+ names(): Promise<string[]>;
131
+ /**
132
+ * Write back a rotated value (OAuth refresh). Tier-bound (ruling 2):
133
+ * writes go only to the account-tier store bound to {account, product} at
134
+ * construction. Source- and global-tier credentials (admin-issued) are
135
+ * structurally unreachable from this path; a write against a name that
136
+ * resolves only from those tiers throws `SecretAccessError`.
137
+ */
138
+ set(name: string, value: string): Promise<void>;
139
+ }
140
+ /**
141
+ * Host-allowlisted egress (ruling 3): origin-only allowlist with explicit
142
+ * `*.` subdomain wildcards, enforced against the RESOLVED URL's origin —
143
+ * redirect targets included. A denied call throws `EgressDeniedError`
144
+ * synchronously, before any network I/O.
145
+ */
146
+ export type IntegrationFetch = (input: string | URL, init?: RequestInit) => Promise<Response>;
147
+ /**
148
+ * Structured, pino-style. The host pre-binds `{integration, accountId,
149
+ * actor}` so every partner log line is attributable.
150
+ */
151
+ export interface IntegrationLogger {
152
+ debug(context: Record<string, unknown>, message: string): void;
153
+ info(context: Record<string, unknown>, message: string): void;
154
+ warn(context: Record<string, unknown>, message: string): void;
155
+ error(context: Record<string, unknown>, message: string): void;
156
+ }
157
+ /**
158
+ * One host-constructed object whose every method is already scoped to
159
+ * `{account, integration}` before partner code runs. Handed to MCP tool
160
+ * `run()` and SDK server handlers alike (ruling 5: one type, one mock).
161
+ */
162
+ export interface IntegrationContext<Schemas extends StorageSchemas = StorageSchemas> {
163
+ /** Identity facts — read-only, informational. Authorization already happened. */
164
+ readonly accountId: string;
165
+ readonly accountSlug: string | null;
166
+ readonly userId: string | null;
167
+ readonly actor: IntegrationActor;
168
+ readonly sourceId: string | null;
169
+ readonly integration: {
170
+ readonly slug: string;
171
+ readonly productId: string;
172
+ };
173
+ readonly timezone: string | null;
174
+ readonly storage: IntegrationStorage<Schemas>;
175
+ readonly secrets: IntegrationSecrets;
176
+ readonly fetch: IntegrationFetch;
177
+ readonly logger: IntegrationLogger;
178
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=capability-context.js.map
@@ -0,0 +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"]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * A dependency-pure structural stand-in for a React component reference.
3
+ *
4
+ * This package validates integration definitions without depending on React
5
+ * (it depends on nothing but zod). A component is, structurally, either a
6
+ * function (function components, and `React.lazy`'s returned function) or a
7
+ * React "exotic" object tagged with `$$typeof` (`memo`, `forwardRef`,
8
+ * `Context.Provider`, …). `@ekanos/sdk` re-narrows these to the real
9
+ * `ComponentType<…>` for authoring DX; the host adapter casts back.
10
+ */
11
+ export type ComponentReference = ((...args: never[]) => unknown) | {
12
+ readonly $$typeof: symbol;
13
+ };
14
+ export declare function isComponentReference(value: unknown): value is ComponentReference;
@@ -0,0 +1,6 @@
1
+ export function isComponentReference(value) {
2
+ if (typeof value === 'function')
3
+ return true;
4
+ return typeof value === 'object' && value !== null && '$$typeof' in value;
5
+ }
6
+ //# sourceMappingURL=component-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-reference.js","sourceRoot":"","sources":["../src/component-reference.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,oBAAoB,CAClC,KAAc;IAEd,IAAI,OAAO,KAAK,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,UAAU,IAAI,KAAK,CAAC;AAC5E,CAAC","sourcesContent":["/**\n * A dependency-pure structural stand-in for a React component reference.\n *\n * This package validates integration definitions without depending on React\n * (it depends on nothing but zod). A component is, structurally, either a\n * function (function components, and `React.lazy`'s returned function) or a\n * React \"exotic\" object tagged with `$$typeof` (`memo`, `forwardRef`,\n * `Context.Provider`, …). `@ekanos/sdk` re-narrows these to the real\n * `ComponentType<…>` for authoring DX; the host adapter casts back.\n */\nexport type ComponentReference =\n | ((...args: never[]) => unknown)\n | { readonly $$typeof: symbol };\n\nexport function isComponentReference(\n value: unknown,\n): value is ComponentReference {\n if (typeof value === 'function') return true;\n return typeof value === 'object' && value !== null && '$$typeof' in value;\n}\n"]}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @ekanos/integration-schema — the canonical, dependency-pure contract for
3
+ * Ekanos integration definitions.
4
+ *
5
+ * ONE zod schema, ONE set of types, imported by BOTH `@ekanos/sdk`
6
+ * (authoring: `defineIntegration()`) and `@kit/integrations-core` (host trust
7
+ * boundary: `registerPartnerIntegration()` re-parses against this schema).
8
+ * This package depends on nothing but zod, so neither consumer forms a cycle
9
+ * and there is no hand-written structural twin to drift (F3/F9/F10).
10
+ *
11
+ * Depends on: zod. Nothing else — no `@kit/*`, no React, no `server-only`.
12
+ *
13
+ * Relative specifiers are extensionless here on purpose. Workspace consumers
14
+ * resolve this package's raw `src/*.ts` through a bundler, so a `./x.js`
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`
17
+ * adds it to `dist/` after the compiler runs — see that file for the full
18
+ * rationale. Do not hand-write `.js` extensions back into this source tree.
19
+ */
20
+ export type { IntegrationActor, IntegrationContext, IntegrationStorage, ScopedStore, IntegrationSecrets, IntegrationFetch, IntegrationLogger, StorageEntry, StorageWriteOptions, StorageSchemas, StorageSchemaMap, StorageScopeSchemas, StorageKeyDeclaration, StorageKeyDeclarationInput, StorageKeySchema, } from './capability-context.js';
21
+ export type { ComponentReference } from './component-reference.js';
22
+ export { isComponentReference } from './component-reference.js';
23
+ export { DNS_NAMESPACE, EKANOS_INTEGRATION_NAMESPACE, derivePlanId, deriveProductId, uuidv5, } from './product-id.js';
24
+ export { WorkspaceTargetSchema, WorkspaceTargetListSchema, WorkspaceTargetSlugSchema, WorkspaceTargetLayoutSchema, createWorkspaceTargetSchema, createWorkspaceTargetListSchema, normalizeWorkspaceTargets, type WorkspaceTargetSchemaOptions, type WorkspaceTargetDefinition, type ResolvedWorkspaceTarget, } from './workspace-target.js';
25
+ export { IntegrationDefinitionSchema, parseIntegrationDefinition, validateIntegrationDefinitions, getDiscoveredToolName, assertPlainDeclaration, deepFreezeDefinition, } from './integration-definition.js';
26
+ 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';
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @ekanos/integration-schema — the canonical, dependency-pure contract for
3
+ * Ekanos integration definitions.
4
+ *
5
+ * ONE zod schema, ONE set of types, imported by BOTH `@ekanos/sdk`
6
+ * (authoring: `defineIntegration()`) and `@kit/integrations-core` (host trust
7
+ * boundary: `registerPartnerIntegration()` re-parses against this schema).
8
+ * This package depends on nothing but zod, so neither consumer forms a cycle
9
+ * and there is no hand-written structural twin to drift (F3/F9/F10).
10
+ *
11
+ * Depends on: zod. Nothing else — no `@kit/*`, no React, no `server-only`.
12
+ *
13
+ * Relative specifiers are extensionless here on purpose. Workspace consumers
14
+ * resolve this package's raw `src/*.ts` through a bundler, so a `./x.js`
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`
17
+ * adds it to `dist/` after the compiler runs — see that file for the full
18
+ * rationale. Do not hand-write `.js` extensions back into this source tree.
19
+ */
20
+ export { isComponentReference } from './component-reference.js';
21
+ // Deterministic slug → uuid derivation. One slug, one product id, everywhere
22
+ // and forever — the SDK, the seed generator and any future gate all agree by
23
+ // computing rather than choosing.
24
+ export { DNS_NAMESPACE, EKANOS_INTEGRATION_NAMESPACE, derivePlanId, deriveProductId, uuidv5, } from './product-id.js';
25
+ // Workspace-target contract (F10): one schema, host injects icon validity
26
+ export { WorkspaceTargetSchema, WorkspaceTargetListSchema, WorkspaceTargetSlugSchema, WorkspaceTargetLayoutSchema, createWorkspaceTargetSchema, createWorkspaceTargetListSchema, normalizeWorkspaceTargets, } from './workspace-target.js';
27
+ // The integration-definition contract
28
+ export { IntegrationDefinitionSchema, parseIntegrationDefinition, validateIntegrationDefinitions, getDiscoveredToolName, assertPlainDeclaration, deepFreezeDefinition, } from './integration-definition.js';
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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,EAC9B,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 getDiscoveredToolName,\n assertPlainDeclaration,\n deepFreezeDefinition,\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"]}