@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 +21 -0
- package/README.md +48 -0
- package/dist/capability-context.d.ts +178 -0
- package/dist/capability-context.js +2 -0
- package/dist/capability-context.js.map +1 -0
- package/dist/component-reference.d.ts +14 -0
- package/dist/component-reference.js +6 -0
- package/dist/component-reference.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/integration-definition.d.ts +1771 -0
- package/dist/integration-definition.js +661 -0
- package/dist/integration-definition.js.map +1 -0
- package/dist/product-id.d.ts +64 -0
- package/dist/product-id.js +201 -0
- package/dist/product-id.js.map +1 -0
- package/dist/workspace-target.d.ts +295 -0
- package/dist/workspace-target.js +150 -0
- package/dist/workspace-target.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The canonical, dependency-pure workspace-target contract (F10): ONE schema
|
|
3
|
+
* for SDK authoring validation, host CI, and runtime normalization — no
|
|
4
|
+
* reduced reimplementation on the SDK side.
|
|
5
|
+
*
|
|
6
|
+
* Icon-NAME validity is the one host-specific rule this package cannot own:
|
|
7
|
+
* whether a string is a real Font Awesome / lucide name lives in the host
|
|
8
|
+
* design system (`@kit/ui/icon-shared`), which is not dependency-pure. So the
|
|
9
|
+
* schema validates the icon structurally (a bounded non-empty string) and
|
|
10
|
+
* accepts an INJECTED `isValidIcon` predicate: the host passes its own
|
|
11
|
+
* (`@kit/integrations-core`'s `workspace-target.schema.ts`), the SDK omits it
|
|
12
|
+
* (a partner's icon set is the host's to judge at the promotion gate). Every
|
|
13
|
+
* other rule — slug format, length bounds, the mutual-exclusivity of
|
|
14
|
+
* `inheritsWidgets`/`includedWidgetIds`, unique slugs, exactly-one-inheriting,
|
|
15
|
+
* the target count cap — is single-sourced here.
|
|
16
|
+
*
|
|
17
|
+
* Ported from `@kit/integrations-core`'s original schema; field constraints
|
|
18
|
+
* mirror the `workspaces` table (`apps/web/supabase/schemas/92-workspaces.sql`).
|
|
19
|
+
*/
|
|
20
|
+
import { z } from 'zod';
|
|
21
|
+
export interface WorkspaceTargetSchemaOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Host-injected icon-name validator. When provided, an icon that fails it
|
|
24
|
+
* is rejected; when omitted (SDK path), only the structural string check
|
|
25
|
+
* applies.
|
|
26
|
+
*/
|
|
27
|
+
isValidIcon?: (icon: string) => boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Stable URL segment — `/workspace/<slug>` routes reference it, so it may
|
|
30
|
+
* never contain anything needing escaping. */
|
|
31
|
+
export declare const WorkspaceTargetSlugSchema: z.ZodString;
|
|
32
|
+
/** Layouts a declaration may select. `'table'` still exists on the DB enum but
|
|
33
|
+
* is dead legacy since plan 038 — no declaration may write it. */
|
|
34
|
+
export declare const WorkspaceTargetLayoutSchema: z.ZodEnum<["masonry", "two-column"]>;
|
|
35
|
+
export declare function createWorkspaceTargetSchema(options?: WorkspaceTargetSchemaOptions): z.ZodEffects<z.ZodObject<{
|
|
36
|
+
slug: z.ZodString;
|
|
37
|
+
name: z.ZodString;
|
|
38
|
+
icon: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
39
|
+
description: z.ZodOptional<z.ZodString>;
|
|
40
|
+
layout: z.ZodDefault<z.ZodEnum<["masonry", "two-column"]>>;
|
|
41
|
+
/**
|
|
42
|
+
* Whether this workspace receives the product's otherwise-unplaced
|
|
43
|
+
* widgets. Exactly one target per product must set it — see the list
|
|
44
|
+
* schema below.
|
|
45
|
+
*/
|
|
46
|
+
inheritsWidgets: z.ZodOptional<z.ZodBoolean>;
|
|
47
|
+
/** Limit this workspace to specific widget ids. */
|
|
48
|
+
includedWidgetIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
49
|
+
}, "strict", z.ZodTypeAny, {
|
|
50
|
+
slug: string;
|
|
51
|
+
name: string;
|
|
52
|
+
layout: "masonry" | "two-column";
|
|
53
|
+
description?: string | undefined;
|
|
54
|
+
icon?: string | undefined;
|
|
55
|
+
inheritsWidgets?: boolean | undefined;
|
|
56
|
+
includedWidgetIds?: string[] | undefined;
|
|
57
|
+
}, {
|
|
58
|
+
slug: string;
|
|
59
|
+
name: string;
|
|
60
|
+
description?: string | undefined;
|
|
61
|
+
icon?: string | undefined;
|
|
62
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
63
|
+
inheritsWidgets?: boolean | undefined;
|
|
64
|
+
includedWidgetIds?: string[] | undefined;
|
|
65
|
+
}>, {
|
|
66
|
+
slug: string;
|
|
67
|
+
name: string;
|
|
68
|
+
layout: "masonry" | "two-column";
|
|
69
|
+
description?: string | undefined;
|
|
70
|
+
icon?: string | undefined;
|
|
71
|
+
inheritsWidgets?: boolean | undefined;
|
|
72
|
+
includedWidgetIds?: string[] | undefined;
|
|
73
|
+
}, {
|
|
74
|
+
slug: string;
|
|
75
|
+
name: string;
|
|
76
|
+
description?: string | undefined;
|
|
77
|
+
icon?: string | undefined;
|
|
78
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
79
|
+
inheritsWidgets?: boolean | undefined;
|
|
80
|
+
includedWidgetIds?: string[] | undefined;
|
|
81
|
+
}>;
|
|
82
|
+
/** Default (SDK-side) target schema: structural, no host icon-name check. */
|
|
83
|
+
export declare const WorkspaceTargetSchema: z.ZodEffects<z.ZodObject<{
|
|
84
|
+
slug: z.ZodString;
|
|
85
|
+
name: z.ZodString;
|
|
86
|
+
icon: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
87
|
+
description: z.ZodOptional<z.ZodString>;
|
|
88
|
+
layout: z.ZodDefault<z.ZodEnum<["masonry", "two-column"]>>;
|
|
89
|
+
/**
|
|
90
|
+
* Whether this workspace receives the product's otherwise-unplaced
|
|
91
|
+
* widgets. Exactly one target per product must set it — see the list
|
|
92
|
+
* schema below.
|
|
93
|
+
*/
|
|
94
|
+
inheritsWidgets: z.ZodOptional<z.ZodBoolean>;
|
|
95
|
+
/** Limit this workspace to specific widget ids. */
|
|
96
|
+
includedWidgetIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
97
|
+
}, "strict", z.ZodTypeAny, {
|
|
98
|
+
slug: string;
|
|
99
|
+
name: string;
|
|
100
|
+
layout: "masonry" | "two-column";
|
|
101
|
+
description?: string | undefined;
|
|
102
|
+
icon?: string | undefined;
|
|
103
|
+
inheritsWidgets?: boolean | undefined;
|
|
104
|
+
includedWidgetIds?: string[] | undefined;
|
|
105
|
+
}, {
|
|
106
|
+
slug: string;
|
|
107
|
+
name: string;
|
|
108
|
+
description?: string | undefined;
|
|
109
|
+
icon?: string | undefined;
|
|
110
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
111
|
+
inheritsWidgets?: boolean | undefined;
|
|
112
|
+
includedWidgetIds?: string[] | undefined;
|
|
113
|
+
}>, {
|
|
114
|
+
slug: string;
|
|
115
|
+
name: string;
|
|
116
|
+
layout: "masonry" | "two-column";
|
|
117
|
+
description?: string | undefined;
|
|
118
|
+
icon?: string | undefined;
|
|
119
|
+
inheritsWidgets?: boolean | undefined;
|
|
120
|
+
includedWidgetIds?: string[] | undefined;
|
|
121
|
+
}, {
|
|
122
|
+
slug: string;
|
|
123
|
+
name: string;
|
|
124
|
+
description?: string | undefined;
|
|
125
|
+
icon?: string | undefined;
|
|
126
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
127
|
+
inheritsWidgets?: boolean | undefined;
|
|
128
|
+
includedWidgetIds?: string[] | undefined;
|
|
129
|
+
}>;
|
|
130
|
+
export type WorkspaceTargetDefinition = z.input<typeof WorkspaceTargetSchema>;
|
|
131
|
+
/** A target after parsing, with `layout` defaulted. */
|
|
132
|
+
export type ResolvedWorkspaceTarget = z.output<typeof WorkspaceTargetSchema>;
|
|
133
|
+
/**
|
|
134
|
+
* A whole product's declaration set. The cross-target invariants (unique
|
|
135
|
+
* slugs, exactly one inheriting target, a max count) live here because none
|
|
136
|
+
* is checkable from inside a single target.
|
|
137
|
+
*/
|
|
138
|
+
export declare function createWorkspaceTargetListSchema(options?: WorkspaceTargetSchemaOptions): z.ZodEffects<z.ZodArray<z.ZodEffects<z.ZodObject<{
|
|
139
|
+
slug: z.ZodString;
|
|
140
|
+
name: z.ZodString;
|
|
141
|
+
icon: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
142
|
+
description: z.ZodOptional<z.ZodString>;
|
|
143
|
+
layout: z.ZodDefault<z.ZodEnum<["masonry", "two-column"]>>;
|
|
144
|
+
/**
|
|
145
|
+
* Whether this workspace receives the product's otherwise-unplaced
|
|
146
|
+
* widgets. Exactly one target per product must set it — see the list
|
|
147
|
+
* schema below.
|
|
148
|
+
*/
|
|
149
|
+
inheritsWidgets: z.ZodOptional<z.ZodBoolean>;
|
|
150
|
+
/** Limit this workspace to specific widget ids. */
|
|
151
|
+
includedWidgetIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
152
|
+
}, "strict", z.ZodTypeAny, {
|
|
153
|
+
slug: string;
|
|
154
|
+
name: string;
|
|
155
|
+
layout: "masonry" | "two-column";
|
|
156
|
+
description?: string | undefined;
|
|
157
|
+
icon?: string | undefined;
|
|
158
|
+
inheritsWidgets?: boolean | undefined;
|
|
159
|
+
includedWidgetIds?: string[] | undefined;
|
|
160
|
+
}, {
|
|
161
|
+
slug: string;
|
|
162
|
+
name: string;
|
|
163
|
+
description?: string | undefined;
|
|
164
|
+
icon?: string | undefined;
|
|
165
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
166
|
+
inheritsWidgets?: boolean | undefined;
|
|
167
|
+
includedWidgetIds?: string[] | undefined;
|
|
168
|
+
}>, {
|
|
169
|
+
slug: string;
|
|
170
|
+
name: string;
|
|
171
|
+
layout: "masonry" | "two-column";
|
|
172
|
+
description?: string | undefined;
|
|
173
|
+
icon?: string | undefined;
|
|
174
|
+
inheritsWidgets?: boolean | undefined;
|
|
175
|
+
includedWidgetIds?: string[] | undefined;
|
|
176
|
+
}, {
|
|
177
|
+
slug: string;
|
|
178
|
+
name: string;
|
|
179
|
+
description?: string | undefined;
|
|
180
|
+
icon?: string | undefined;
|
|
181
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
182
|
+
inheritsWidgets?: boolean | undefined;
|
|
183
|
+
includedWidgetIds?: string[] | undefined;
|
|
184
|
+
}>, "many">, {
|
|
185
|
+
slug: string;
|
|
186
|
+
name: string;
|
|
187
|
+
layout: "masonry" | "two-column";
|
|
188
|
+
description?: string | undefined;
|
|
189
|
+
icon?: string | undefined;
|
|
190
|
+
inheritsWidgets?: boolean | undefined;
|
|
191
|
+
includedWidgetIds?: string[] | undefined;
|
|
192
|
+
}[], {
|
|
193
|
+
slug: string;
|
|
194
|
+
name: string;
|
|
195
|
+
description?: string | undefined;
|
|
196
|
+
icon?: string | undefined;
|
|
197
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
198
|
+
inheritsWidgets?: boolean | undefined;
|
|
199
|
+
includedWidgetIds?: string[] | undefined;
|
|
200
|
+
}[]>;
|
|
201
|
+
export declare const WorkspaceTargetListSchema: z.ZodEffects<z.ZodArray<z.ZodEffects<z.ZodObject<{
|
|
202
|
+
slug: z.ZodString;
|
|
203
|
+
name: z.ZodString;
|
|
204
|
+
icon: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
205
|
+
description: z.ZodOptional<z.ZodString>;
|
|
206
|
+
layout: z.ZodDefault<z.ZodEnum<["masonry", "two-column"]>>;
|
|
207
|
+
/**
|
|
208
|
+
* Whether this workspace receives the product's otherwise-unplaced
|
|
209
|
+
* widgets. Exactly one target per product must set it — see the list
|
|
210
|
+
* schema below.
|
|
211
|
+
*/
|
|
212
|
+
inheritsWidgets: z.ZodOptional<z.ZodBoolean>;
|
|
213
|
+
/** Limit this workspace to specific widget ids. */
|
|
214
|
+
includedWidgetIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
215
|
+
}, "strict", z.ZodTypeAny, {
|
|
216
|
+
slug: string;
|
|
217
|
+
name: string;
|
|
218
|
+
layout: "masonry" | "two-column";
|
|
219
|
+
description?: string | undefined;
|
|
220
|
+
icon?: string | undefined;
|
|
221
|
+
inheritsWidgets?: boolean | undefined;
|
|
222
|
+
includedWidgetIds?: string[] | undefined;
|
|
223
|
+
}, {
|
|
224
|
+
slug: string;
|
|
225
|
+
name: string;
|
|
226
|
+
description?: string | undefined;
|
|
227
|
+
icon?: string | undefined;
|
|
228
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
229
|
+
inheritsWidgets?: boolean | undefined;
|
|
230
|
+
includedWidgetIds?: string[] | undefined;
|
|
231
|
+
}>, {
|
|
232
|
+
slug: string;
|
|
233
|
+
name: string;
|
|
234
|
+
layout: "masonry" | "two-column";
|
|
235
|
+
description?: string | undefined;
|
|
236
|
+
icon?: string | undefined;
|
|
237
|
+
inheritsWidgets?: boolean | undefined;
|
|
238
|
+
includedWidgetIds?: string[] | undefined;
|
|
239
|
+
}, {
|
|
240
|
+
slug: string;
|
|
241
|
+
name: string;
|
|
242
|
+
description?: string | undefined;
|
|
243
|
+
icon?: string | undefined;
|
|
244
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
245
|
+
inheritsWidgets?: boolean | undefined;
|
|
246
|
+
includedWidgetIds?: string[] | undefined;
|
|
247
|
+
}>, "many">, {
|
|
248
|
+
slug: string;
|
|
249
|
+
name: string;
|
|
250
|
+
layout: "masonry" | "two-column";
|
|
251
|
+
description?: string | undefined;
|
|
252
|
+
icon?: string | undefined;
|
|
253
|
+
inheritsWidgets?: boolean | undefined;
|
|
254
|
+
includedWidgetIds?: string[] | undefined;
|
|
255
|
+
}[], {
|
|
256
|
+
slug: string;
|
|
257
|
+
name: string;
|
|
258
|
+
description?: string | undefined;
|
|
259
|
+
icon?: string | undefined;
|
|
260
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
261
|
+
inheritsWidgets?: boolean | undefined;
|
|
262
|
+
includedWidgetIds?: string[] | undefined;
|
|
263
|
+
}[]>;
|
|
264
|
+
/**
|
|
265
|
+
* Parses a declaration set and fills in the one benign omission the list
|
|
266
|
+
* schema tolerates (a single non-subset target that never set
|
|
267
|
+
* `inheritsWidgets`). Returns the issues instead of throwing so callers
|
|
268
|
+
* choose their own failure mode — CI fails the build, materialization logs
|
|
269
|
+
* and skips.
|
|
270
|
+
*/
|
|
271
|
+
export declare function normalizeWorkspaceTargets(declared: unknown, options?: WorkspaceTargetSchemaOptions): {
|
|
272
|
+
ok: false;
|
|
273
|
+
error: z.ZodError<{
|
|
274
|
+
slug: string;
|
|
275
|
+
name: string;
|
|
276
|
+
description?: string | undefined;
|
|
277
|
+
icon?: string | undefined;
|
|
278
|
+
layout?: "masonry" | "two-column" | undefined;
|
|
279
|
+
inheritsWidgets?: boolean | undefined;
|
|
280
|
+
includedWidgetIds?: string[] | undefined;
|
|
281
|
+
}[]>;
|
|
282
|
+
targets?: undefined;
|
|
283
|
+
} | {
|
|
284
|
+
ok: true;
|
|
285
|
+
targets: {
|
|
286
|
+
slug: string;
|
|
287
|
+
name: string;
|
|
288
|
+
layout: "masonry" | "two-column";
|
|
289
|
+
description?: string | undefined;
|
|
290
|
+
icon?: string | undefined;
|
|
291
|
+
inheritsWidgets?: boolean | undefined;
|
|
292
|
+
includedWidgetIds?: string[] | undefined;
|
|
293
|
+
}[];
|
|
294
|
+
error?: undefined;
|
|
295
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The canonical, dependency-pure workspace-target contract (F10): ONE schema
|
|
3
|
+
* for SDK authoring validation, host CI, and runtime normalization — no
|
|
4
|
+
* reduced reimplementation on the SDK side.
|
|
5
|
+
*
|
|
6
|
+
* Icon-NAME validity is the one host-specific rule this package cannot own:
|
|
7
|
+
* whether a string is a real Font Awesome / lucide name lives in the host
|
|
8
|
+
* design system (`@kit/ui/icon-shared`), which is not dependency-pure. So the
|
|
9
|
+
* schema validates the icon structurally (a bounded non-empty string) and
|
|
10
|
+
* accepts an INJECTED `isValidIcon` predicate: the host passes its own
|
|
11
|
+
* (`@kit/integrations-core`'s `workspace-target.schema.ts`), the SDK omits it
|
|
12
|
+
* (a partner's icon set is the host's to judge at the promotion gate). Every
|
|
13
|
+
* other rule — slug format, length bounds, the mutual-exclusivity of
|
|
14
|
+
* `inheritsWidgets`/`includedWidgetIds`, unique slugs, exactly-one-inheriting,
|
|
15
|
+
* the target count cap — is single-sourced here.
|
|
16
|
+
*
|
|
17
|
+
* Ported from `@kit/integrations-core`'s original schema; field constraints
|
|
18
|
+
* mirror the `workspaces` table (`apps/web/supabase/schemas/92-workspaces.sql`).
|
|
19
|
+
*/
|
|
20
|
+
import { z } from 'zod';
|
|
21
|
+
/** Stable URL segment — `/workspace/<slug>` routes reference it, so it may
|
|
22
|
+
* never contain anything needing escaping. */
|
|
23
|
+
export const WorkspaceTargetSlugSchema = z
|
|
24
|
+
.string()
|
|
25
|
+
.trim()
|
|
26
|
+
.min(1, 'Slug is required')
|
|
27
|
+
.max(100)
|
|
28
|
+
.regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'Must be lowercase alphanumeric with single hyphens between segments');
|
|
29
|
+
/** Layouts a declaration may select. `'table'` still exists on the DB enum but
|
|
30
|
+
* is dead legacy since plan 038 — no declaration may write it. */
|
|
31
|
+
export const WorkspaceTargetLayoutSchema = z.enum(['masonry', 'two-column']);
|
|
32
|
+
export function createWorkspaceTargetSchema(options = {}) {
|
|
33
|
+
const iconSchema = z
|
|
34
|
+
.string()
|
|
35
|
+
.trim()
|
|
36
|
+
.min(1)
|
|
37
|
+
.max(100)
|
|
38
|
+
.refine((icon) => { var _a, _b; return (_b = (_a = options.isValidIcon) === null || _a === void 0 ? void 0 : _a.call(options, icon)) !== null && _b !== void 0 ? _b : true; }, {
|
|
39
|
+
message: 'Must be a valid icon',
|
|
40
|
+
});
|
|
41
|
+
return z
|
|
42
|
+
.object({
|
|
43
|
+
slug: WorkspaceTargetSlugSchema,
|
|
44
|
+
name: z.string().trim().min(1, 'Name is required').max(100),
|
|
45
|
+
icon: iconSchema.optional(),
|
|
46
|
+
description: z.string().trim().max(500).optional(),
|
|
47
|
+
layout: WorkspaceTargetLayoutSchema.default('masonry'),
|
|
48
|
+
/**
|
|
49
|
+
* Whether this workspace receives the product's otherwise-unplaced
|
|
50
|
+
* widgets. Exactly one target per product must set it — see the list
|
|
51
|
+
* schema below.
|
|
52
|
+
*/
|
|
53
|
+
inheritsWidgets: z.boolean().optional(),
|
|
54
|
+
/** Limit this workspace to specific widget ids. */
|
|
55
|
+
includedWidgetIds: z.array(z.string().trim().min(1)).max(100).optional(),
|
|
56
|
+
})
|
|
57
|
+
.strict()
|
|
58
|
+
.superRefine((target, ctx) => {
|
|
59
|
+
var _a;
|
|
60
|
+
if (target.inheritsWidgets && target.includedWidgetIds) {
|
|
61
|
+
ctx.addIssue({
|
|
62
|
+
code: z.ZodIssueCode.custom,
|
|
63
|
+
path: ['includedWidgetIds'],
|
|
64
|
+
message: 'inheritsWidgets and includedWidgetIds are mutually exclusive — a widget-subset workspace cannot also inherit unplaced widgets',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (((_a = target.includedWidgetIds) === null || _a === void 0 ? void 0 : _a.length) === 0) {
|
|
68
|
+
ctx.addIssue({
|
|
69
|
+
code: z.ZodIssueCode.custom,
|
|
70
|
+
path: ['includedWidgetIds'],
|
|
71
|
+
message: 'includedWidgetIds must list at least one widget id — omit the field for a workspace that inherits instead',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/** Default (SDK-side) target schema: structural, no host icon-name check. */
|
|
77
|
+
export const WorkspaceTargetSchema = createWorkspaceTargetSchema();
|
|
78
|
+
function refineTargetList(targets, ctx) {
|
|
79
|
+
const seenSlugs = new Set();
|
|
80
|
+
for (const [index, target] of targets.entries()) {
|
|
81
|
+
if (seenSlugs.has(target.slug)) {
|
|
82
|
+
ctx.addIssue({
|
|
83
|
+
code: z.ZodIssueCode.custom,
|
|
84
|
+
path: [index, 'slug'],
|
|
85
|
+
message: `Duplicate workspace slug "${target.slug}" — every target in one declaration set needs its own slug`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
seenSlugs.add(target.slug);
|
|
89
|
+
}
|
|
90
|
+
if (targets.length === 0)
|
|
91
|
+
return;
|
|
92
|
+
const canInherit = (target) => !target.includedWidgetIds;
|
|
93
|
+
const inheriting = targets.filter((target) => canInherit(target) && target.inheritsWidgets);
|
|
94
|
+
if (inheriting.length === 1)
|
|
95
|
+
return;
|
|
96
|
+
// A lone non-subset target is unambiguous — `normalizeWorkspaceTargets`
|
|
97
|
+
// fills the flag in for it.
|
|
98
|
+
if (inheriting.length === 0 &&
|
|
99
|
+
targets.length === 1 &&
|
|
100
|
+
canInherit(targets[0])) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (inheriting.length === 0) {
|
|
104
|
+
ctx.addIssue({
|
|
105
|
+
code: z.ZodIssueCode.custom,
|
|
106
|
+
message: 'No target can receive this product’s unplaced widgets — exactly one target without includedWidgetIds must set inheritsWidgets: true',
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
ctx.addIssue({
|
|
111
|
+
code: z.ZodIssueCode.custom,
|
|
112
|
+
message: `${inheriting.length} targets set inheritsWidgets — exactly one must, since a product has at most one inheriting attachment per scope`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A whole product's declaration set. The cross-target invariants (unique
|
|
117
|
+
* slugs, exactly one inheriting target, a max count) live here because none
|
|
118
|
+
* is checkable from inside a single target.
|
|
119
|
+
*/
|
|
120
|
+
export function createWorkspaceTargetListSchema(options = {}) {
|
|
121
|
+
return z
|
|
122
|
+
.array(createWorkspaceTargetSchema(options))
|
|
123
|
+
.max(20)
|
|
124
|
+
.superRefine(refineTargetList);
|
|
125
|
+
}
|
|
126
|
+
export const WorkspaceTargetListSchema = createWorkspaceTargetListSchema();
|
|
127
|
+
/**
|
|
128
|
+
* Parses a declaration set and fills in the one benign omission the list
|
|
129
|
+
* schema tolerates (a single non-subset target that never set
|
|
130
|
+
* `inheritsWidgets`). Returns the issues instead of throwing so callers
|
|
131
|
+
* choose their own failure mode — CI fails the build, materialization logs
|
|
132
|
+
* and skips.
|
|
133
|
+
*/
|
|
134
|
+
export function normalizeWorkspaceTargets(declared, options = {}) {
|
|
135
|
+
const parsed = createWorkspaceTargetListSchema(options).safeParse(declared);
|
|
136
|
+
if (!parsed.success) {
|
|
137
|
+
return { ok: false, error: parsed.error };
|
|
138
|
+
}
|
|
139
|
+
const targets = parsed.data;
|
|
140
|
+
if (targets.length === 1 &&
|
|
141
|
+
!targets[0].includedWidgetIds &&
|
|
142
|
+
!targets[0].inheritsWidgets) {
|
|
143
|
+
return {
|
|
144
|
+
ok: true,
|
|
145
|
+
targets: [Object.assign(Object.assign({}, targets[0]), { inheritsWidgets: true })],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return { ok: true, targets };
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=workspace-target.js.map
|
|
@@ -0,0 +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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ekanos/integration-schema",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
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
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/companydotcom/fusion.git",
|
|
10
|
+
"directory": "packages/integration-schema"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/companydotcom/fusion/tree/dev/packages/integration-schema#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/companydotcom/fusion/issues"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"zod": "^3.25.76"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "^5.9.3",
|
|
35
|
+
"vitest": "4.1.10",
|
|
36
|
+
"zod": "^3.25.74",
|
|
37
|
+
"@kit/eslint-config": "0.2.0",
|
|
38
|
+
"@kit/prettier-config": "0.1.0",
|
|
39
|
+
"@kit/tsconfig": "0.1.0"
|
|
40
|
+
},
|
|
41
|
+
"prettier": "@kit/prettier-config",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.build.json && node scripts/rewrite-esm-specifiers.mjs",
|
|
44
|
+
"clean": "git clean -xdf .turbo node_modules dist",
|
|
45
|
+
"format": "prettier --check \"**/*.{ts,tsx}\"",
|
|
46
|
+
"lint": "eslint .",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"test": "vitest run --config vitest.config.ts"
|
|
49
|
+
},
|
|
50
|
+
"main": "./dist/index.js",
|
|
51
|
+
"module": "./dist/index.js",
|
|
52
|
+
"types": "./dist/index.d.ts"
|
|
53
|
+
}
|