@sitecoreai-labs/sitecoreai-cli 0.1.2 → 0.2.1
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/dist/agents/tasks/agent.js +2 -2
- package/dist/agents/tasks/resources.js +2 -2
- package/dist/agents/tasks/space.js +1 -1
- package/dist/brand/api/auth.d.ts +16 -9
- package/dist/brand/api/auth.js +29 -19
- package/dist/brand/credential.d.ts +71 -1
- package/dist/brand/credential.js +119 -2
- package/dist/brand/recipe/diff.js +7 -2
- package/dist/brand/recipe/kind.js +0 -0
- package/dist/brand/recipe/schema.d.ts +113 -7
- package/dist/brand/recipe/schema.js +137 -8
- package/dist/brand/seed.d.ts +9 -5
- package/dist/brand/seed.js +30 -5
- package/dist/brief/api/briefs.d.ts +8 -0
- package/dist/brief/api/briefs.js +49 -11
- package/dist/brief/index.d.ts +1 -1
- package/dist/brief/index.js +2 -1
- package/dist/brief/recipe/index.d.ts +11 -2
- package/dist/brief/recipe/index.js +17 -3
- package/dist/brief/recipe/instance-diff.d.ts +4 -0
- package/dist/brief/recipe/instance-diff.js +77 -0
- package/dist/brief/recipe/instance-kind.d.ts +4 -0
- package/dist/brief/recipe/instance-kind.js +190 -0
- package/dist/brief/recipe/instance-schema.d.ts +61 -0
- package/dist/brief/recipe/instance-schema.js +68 -0
- package/dist/brief/recipe/schema.js +4 -1
- package/dist/brief/tasks/index.d.ts +35 -0
- package/dist/brief/tasks/index.js +62 -1
- package/dist/campaigns/recipe/schema.d.ts +39 -8
- package/dist/campaigns/recipe/schema.js +40 -10
- package/dist/commands/agents/sync.js +2 -2
- package/dist/commands/brand/seed.js +1 -1
- package/dist/commands/brand/sync.js +2 -2
- package/dist/commands/brief/create.d.ts +2 -0
- package/dist/commands/brief/create.js +56 -0
- package/dist/commands/brief/index.d.ts +3 -1
- package/dist/commands/brief/index.js +11 -1
- package/dist/commands/brief/sync.d.ts +9 -6
- package/dist/commands/brief/sync.js +54 -22
- package/dist/commands/brief/update.d.ts +2 -0
- package/dist/commands/brief/update.js +84 -0
- package/dist/commands/campaign/sync.js +2 -2
- package/dist/mcp/descriptions.js +3 -3
- package/dist/mcp/tools/brief-recipe.js +67 -23
- package/dist/mcp/tools/brief.js +83 -6
- package/dist/recipe/compile/design-parameters-template.js +5 -3
- package/dist/recipe/compile/enumeration.js +6 -11
- package/dist/recipe/compile/shared.js +12 -12
- package/dist/recipe/compile.js +4 -4
- package/dist/recipe/io.d.ts +8 -3
- package/dist/recipe/io.js +11 -81
- package/dist/recipe/items/read-current.js +31 -24
- package/dist/recipe/schema/recipe.d.ts +167 -84
- package/dist/recipe/schema/recipe.js +130 -46
- package/dist/recipe/schema/source-fields.d.ts +20 -0
- package/dist/recipe/schema/source-fields.js +25 -1
- package/dist/recipe/validate.d.ts +3 -3
- package/dist/recipe/validate.js +20 -10
- package/dist/sync/aggregate-kinds.js +1 -0
- package/dist/sync/aggregate.js +2 -2
- package/dist/sync/io.d.ts +13 -3
- package/dist/sync/io.js +43 -17
- package/dist/sync/typescript-recipe.d.ts +30 -0
- package/dist/sync/typescript-recipe.js +112 -0
- package/package.json +1 -1
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RecipeSchema = exports.WorkflowRecipeSchema = exports.WebhookAuthorizationRecipeSchema = exports.EnumerationRecipeSchema = exports.EnumerationValueSchema = exports.SiteRecipeSchema = exports.SiteGroupingSchema = exports.SiteTemplateRecipeSchema = exports.SiteTemplateTaxonomyEntrySchema = exports.SiteTemplateDictionaryEntrySchema = exports.PageDesignRecipeSchema = exports.PartialDesignRecipeSchema = exports.PageRecipeSchema = exports.PageTemplateRecipeSchema = exports.LayoutSchema = exports.ComponentPlacementSchema = exports.ContentItemRecipeSchema = exports.ContentVersionSchema = exports.ContentVariantSchema = exports.ContentTranslationSchema = exports.ContentFieldValueSchema = exports.SectionDefinitionRecipeSchema = exports.DesignParametersTemplateRecipeSchema = exports.ContentTemplateRecipeSchema = exports.RecipeMetaSchema = exports.RecipeMetaTaxSchema = exports.ComponentTemplateRecipeSchema = exports.ComponentSectionRecipeSchema = exports.RecipeDatasourceSchema = exports.RenderingDatasourceLocationSchema = exports.PlaceholderRecipeSchema = exports.PlaceholderDefinitionSchema = exports.RenderingVariantDefinitionSchema = exports.DesignParameterSchema = exports.FieldDefinitionSchema = exports.SitecoreFieldAugmentSchema = void 0;
|
|
3
|
+
exports.RecipeSchema = exports.WorkflowRecipeSchema = exports.WebhookAuthorizationRecipeSchema = exports.EnumerationRecipeSchema = exports.EnumerationValueSchema = exports.SiteRecipeSchema = exports.SiteGroupingSchema = exports.SiteTemplateRecipeSchema = exports.SiteTemplateTaxonomyEntrySchema = exports.SiteTemplateDictionaryEntrySchema = exports.PageDesignRecipeSchema = exports.PartialDesignRecipeSchema = exports.PageRecipeSchema = exports.PageTemplateRecipeSchema = exports.LayoutSchema = exports.ComponentPlacementSchema = exports.ContentItemRecipeSchema = exports.ContentVersionSchema = exports.ContentVariantSchema = exports.ContentTranslationSchema = exports.ContentFieldValueSchema = exports.SectionDefinitionRecipeSchema = exports.DesignParametersTemplateRecipeSchema = exports.ContentTemplateRecipeSchema = exports.RecipeMetaSchema = exports.RecipeMetaTaxSchema = exports.ComponentTemplateRecipeSchema = exports.ComponentSectionRecipeSchema = exports.RecipeDatasourceSchema = exports.RenderingDatasourceLocationSchema = exports.PlaceholderRecipeSchema = exports.PlaceholderDefinitionSchema = exports.RenderingVariantDefinitionSchema = exports.DesignParameterSchema = exports.FieldDefinitionSchema = exports.SitecoreFieldAugmentSchema = exports.SitecoreFieldSourceSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const field_types_1 = require("./field-types");
|
|
6
|
+
/**
|
|
7
|
+
* Multi-segment folder path accepted by `location.folder` /
|
|
8
|
+
* `placeholder.folder`. Two wire shapes:
|
|
9
|
+
*
|
|
10
|
+
* Array form (canonical): `["Theme", "Color"]`
|
|
11
|
+
* Slash-string form (legacy): `"Theme/Color"`
|
|
12
|
+
*
|
|
13
|
+
* Both normalize to `string[]` after parsing. The registry moved its
|
|
14
|
+
* recipe schema to array form because the slash-string was implicit
|
|
15
|
+
* and fragile to author through Agent Studio (no IDE help for the
|
|
16
|
+
* segments inside the string); scai accepts both so old recipes keep
|
|
17
|
+
* working and new ones use the explicit shape. Empty segments
|
|
18
|
+
* (`""` / `"a//b"`) after split + trim are filtered out so callers
|
|
19
|
+
* don't have to remember to clean them.
|
|
20
|
+
*
|
|
21
|
+
* Downstream consumers (compile/enumeration, compile/placeholder,
|
|
22
|
+
* read-current) all see `string[]` and don't need to split anything
|
|
23
|
+
* themselves.
|
|
24
|
+
*/
|
|
25
|
+
const FolderPath = zod_1.z
|
|
26
|
+
.union([zod_1.z.string().min(1), zod_1.z.array(zod_1.z.string().min(1)).min(1)])
|
|
27
|
+
.transform((value) => {
|
|
28
|
+
const segments = (Array.isArray(value) ? value : value.split("/")).map((s) => s.trim());
|
|
29
|
+
return segments.filter((s) => s.length > 0);
|
|
30
|
+
})
|
|
31
|
+
.pipe(zod_1.z.array(zod_1.z.string().min(1)).min(1));
|
|
6
32
|
/**
|
|
7
33
|
* Recipe author surface — what users hand-author for one Sitecore template.
|
|
8
34
|
*
|
|
@@ -41,48 +67,77 @@ const field_types_1 = require("./field-types");
|
|
|
41
67
|
*/
|
|
42
68
|
const HANDLE_PATTERN = /^[a-z][a-z0-9-]*@[0-9]+$/;
|
|
43
69
|
/**
|
|
44
|
-
*
|
|
70
|
+
* The picker-scope source ("Sitecore's Source field") expressed as a
|
|
71
|
+
* discriminated union over the two real modes:
|
|
45
72
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* `DataSource=<path>&IncludeTemplatesForSelection
|
|
73
|
+
* - `filter` — composable structured fields. `types` is a picker
|
|
74
|
+
* filter restricting which recipe-defined templates appear; `query`
|
|
75
|
+
* is a Sitecore Query; `scope` is a fixed content-tree path. They
|
|
76
|
+
* combine, e.g. `scope + types` → `DataSource=<path>&IncludeTemplatesForSelection=...`.
|
|
77
|
+
* - `raw` — verbatim Source string, the escape hatch. Use when the
|
|
78
|
+
* structured surface doesn't fit (e.g. a bare path Treelist source
|
|
79
|
+
* like `/sitecore/content/Tags`).
|
|
80
|
+
*
|
|
81
|
+
* Previously the four fields were peers on `SitecoreFieldAugment` with
|
|
82
|
+
* a `.refine` enforcing the mutex; the union makes the constraint
|
|
83
|
+
* structural so JSON Schema's `oneOf` expresses it natively and Agent
|
|
84
|
+
* Studio can't emit an invalid combination. See
|
|
85
|
+
* `docs/recipe-schema-audit.md` (A1).
|
|
86
|
+
*/
|
|
87
|
+
exports.SitecoreFieldSourceSchema = zod_1.z.discriminatedUnion("kind", [
|
|
88
|
+
zod_1.z.object({
|
|
89
|
+
kind: zod_1.z.literal("filter"),
|
|
90
|
+
types: zod_1.z
|
|
91
|
+
.array(zod_1.z.string())
|
|
92
|
+
.min(1)
|
|
93
|
+
.optional()
|
|
94
|
+
.describe("Picker filter: restrict to items conforming to one of these recipe handles. Compiler resolves each handle to its deterministic template GUID and emits `IncludeTemplatesForSelection={GUID},{GUID}`."),
|
|
95
|
+
query: zod_1.z
|
|
96
|
+
.string()
|
|
97
|
+
.optional()
|
|
98
|
+
.describe("Where to look: a Sitecore Query (e.g. `$site/*[@@name='Data']`). Standalone becomes `query:<query>`; combined with `types` becomes `DataSource=query:<query>&IncludeTemplatesForSelection=...`."),
|
|
99
|
+
scope: zod_1.z
|
|
100
|
+
.string()
|
|
101
|
+
.optional()
|
|
102
|
+
.describe("Where to look: a fixed Sitecore content-tree path. Emitted as `DataSource=<path>`, alone or combined with `types`."),
|
|
103
|
+
}),
|
|
104
|
+
zod_1.z.object({
|
|
105
|
+
kind: zod_1.z.literal("raw"),
|
|
106
|
+
value: zod_1.z
|
|
107
|
+
.string()
|
|
108
|
+
.min(1)
|
|
109
|
+
.describe("Verbatim Sitecore Source string. Escape hatch for Source shapes that don't fit the `filter` mode (e.g. a bare path Treelist source like `/sitecore/content/Tags`)."),
|
|
110
|
+
}),
|
|
111
|
+
]);
|
|
112
|
+
/**
|
|
113
|
+
* Sitecore-side override on a field or param. Defaults apply when
|
|
114
|
+
* omitted.
|
|
50
115
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
|
|
116
|
+
* `source` carries the picker-scope shape as a discriminated union
|
|
117
|
+
* (`filter` | `raw`) — see `SitecoreFieldSourceSchema`. Internal
|
|
118
|
+
* scai code converts to a flat `SourceFields` bag via
|
|
119
|
+
* `augmentSourceToFields()` before passing to `renderSourceFields()`.
|
|
120
|
+
*/
|
|
121
|
+
/**
|
|
122
|
+
* Defensive guard: pre-A1 recipes carried `sourceTypes` / `sourceQuery` /
|
|
123
|
+
* `sourceScope` / `sourceRaw` as peer optional fields on the augment.
|
|
124
|
+
* After A1 the picker scope lives inside a discriminated `source`
|
|
125
|
+
* union. Without this guard Zod's default `.strip()` would silently
|
|
126
|
+
* drop the legacy keys and produce a parsed augment with no `source`
|
|
127
|
+
* — losing author intent quietly. Reject explicitly with a migration
|
|
128
|
+
* pointer instead.
|
|
56
129
|
*/
|
|
130
|
+
const LEGACY_SOURCE_KEYS = ["sourceTypes", "sourceQuery", "sourceScope", "sourceRaw"];
|
|
57
131
|
exports.SitecoreFieldAugmentSchema = zod_1.z
|
|
58
132
|
.object({
|
|
59
133
|
/** Override the default shape→Sitecore type mapping. */
|
|
60
134
|
type: field_types_1.SitecoreFieldTypeSchema.optional(),
|
|
61
135
|
/**
|
|
62
|
-
* Picker
|
|
63
|
-
*
|
|
64
|
-
*
|
|
136
|
+
* Picker scope — discriminated union of two modes: `filter`
|
|
137
|
+
* (composable `types` / `query` / `scope`) or `raw` (verbatim
|
|
138
|
+
* Source string). See `SitecoreFieldSourceSchema`.
|
|
65
139
|
*/
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Where to look: a Sitecore Query (e.g. `$site/*[@@name='Data']`).
|
|
69
|
-
* Standalone, becomes the entire Source as `query:<query>` (the
|
|
70
|
-
* shorthand Sitecore evaluates directly for Droplist-style fields).
|
|
71
|
-
* Combined with `sourceTypes`, becomes `DataSource=query:<query>&...`.
|
|
72
|
-
*/
|
|
73
|
-
sourceQuery: zod_1.z.string().optional(),
|
|
74
|
-
/**
|
|
75
|
-
* Where to look: a fixed Sitecore content-tree path. Emitted as
|
|
76
|
-
* `DataSource=<path>`, alone or combined with `sourceTypes`.
|
|
77
|
-
*/
|
|
78
|
-
sourceScope: zod_1.z.string().optional(),
|
|
79
|
-
/**
|
|
80
|
-
* Escape hatch: verbatim Source string. Mutually exclusive with the
|
|
81
|
-
* structured fields above. Use when you need a Source form that
|
|
82
|
-
* doesn't fit the structured surface (e.g. a bare path Treelist
|
|
83
|
-
* source like `/sitecore/content/Tags`).
|
|
84
|
-
*/
|
|
85
|
-
sourceRaw: zod_1.z.string().optional(),
|
|
140
|
+
source: exports.SitecoreFieldSourceSchema.optional(),
|
|
86
141
|
/** Author-facing hint surfaced in the CMS. */
|
|
87
142
|
hint: zod_1.z.string().optional(),
|
|
88
143
|
/** Required marker (translates to a Sitecore validation rule). */
|
|
@@ -126,10 +181,21 @@ exports.SitecoreFieldAugmentSchema = zod_1.z
|
|
|
126
181
|
*/
|
|
127
182
|
storage: zod_1.z.enum(["versioned", "unversioned", "shared"]).optional(),
|
|
128
183
|
})
|
|
129
|
-
.
|
|
130
|
-
(
|
|
131
|
-
|
|
132
|
-
|
|
184
|
+
.passthrough()
|
|
185
|
+
.superRefine((augment, ctx) => {
|
|
186
|
+
// Pre-A1 recipes carried sourceTypes/sourceQuery/sourceScope/sourceRaw
|
|
187
|
+
// as peer fields; the new shape is `source: { kind, ... }`. Without
|
|
188
|
+
// `.passthrough()` Zod's default `.strip()` would drop those keys
|
|
189
|
+
// before the refine runs; with passthrough they survive to here and
|
|
190
|
+
// we reject loudly with a migration pointer.
|
|
191
|
+
const legacyPresent = LEGACY_SOURCE_KEYS.filter((key) => augment[key] !== undefined);
|
|
192
|
+
if (legacyPresent.length > 0) {
|
|
193
|
+
ctx.addIssue({
|
|
194
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
195
|
+
path: [legacyPresent[0]],
|
|
196
|
+
message: `Legacy source field(s) [${legacyPresent.join(", ")}] are no longer accepted on \`sitecore\`. Move them into the new \`source\` discriminated union: \`source: { kind: "filter", types/query/scope }\` or \`source: { kind: "raw", value }\`. See docs/recipe-schema-audit.md (A1).`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
133
199
|
});
|
|
134
200
|
exports.FieldDefinitionSchema = zod_1.z.object({
|
|
135
201
|
name: zod_1.z.string().min(1),
|
|
@@ -209,7 +275,7 @@ exports.PlaceholderDefinitionSchema = zod_1.z.object({
|
|
|
209
275
|
* Insert Options. Recipes naming the same folder share it. Omit → the
|
|
210
276
|
* item lands flat at the root.
|
|
211
277
|
*/
|
|
212
|
-
folder:
|
|
278
|
+
folder: FolderPath.optional(),
|
|
213
279
|
/**
|
|
214
280
|
* SXA dynamic placeholder. When true the host rendering must also set
|
|
215
281
|
* `dynamicPlaceholders: true` so SXA generates per-instance keys; the
|
|
@@ -268,7 +334,7 @@ exports.PlaceholderRecipeSchema = zod_1.z.object({
|
|
|
268
334
|
* Settings Folder` template (inheriting its Insert Options). Recipes
|
|
269
335
|
* naming the same folder share it. Omit → flat at the root.
|
|
270
336
|
*/
|
|
271
|
-
folder:
|
|
337
|
+
folder: FolderPath.optional(),
|
|
272
338
|
/** SXA dynamic placeholder — see `PlaceholderDefinitionSchema.dynamic`. */
|
|
273
339
|
dynamic: zod_1.z.boolean().default(false),
|
|
274
340
|
/**
|
|
@@ -405,7 +471,8 @@ exports.ComponentSectionRecipeSchema = zod_1.z.object({
|
|
|
405
471
|
*/
|
|
406
472
|
sortOrder: zod_1.z.number().int().optional(),
|
|
407
473
|
});
|
|
408
|
-
exports.ComponentTemplateRecipeSchema = zod_1.z
|
|
474
|
+
exports.ComponentTemplateRecipeSchema = zod_1.z
|
|
475
|
+
.object({
|
|
409
476
|
kind: zod_1.z.literal("component-template"),
|
|
410
477
|
schemaVersion: zod_1.z.literal("1"),
|
|
411
478
|
/** Stable identifier of the form `<kebab-name>@<major>`, e.g. `cta-button@1`. */
|
|
@@ -557,7 +624,14 @@ exports.ComponentTemplateRecipeSchema = zod_1.z.object({
|
|
|
557
624
|
* `autoCreate` / `dynamicPlaceholders` — useful for the rare case
|
|
558
625
|
* where you need to force a specific value.
|
|
559
626
|
*/
|
|
560
|
-
otherProperties: zod_1.z
|
|
627
|
+
otherProperties: zod_1.z
|
|
628
|
+
.record(zod_1.z.string(), zod_1.z.string())
|
|
629
|
+
.optional()
|
|
630
|
+
.describe("Free-form key/value pairs encoded into the rendering's `OtherProperties` URL-encoded shared field. Reserved keys `IsAutoDatasourceRendering` and `IsRenderingsWithDynamicPlaceholders` should normally be set via the typed `datasource.autoCreate` and `dynamicPlaceholders` shortcuts — overriding here silently wins and is intended only for the rare escape-hatch case."),
|
|
631
|
+
})
|
|
632
|
+
.refine((recipe) => !(recipe.parameters !== undefined && recipe.params.length > 0), {
|
|
633
|
+
message: "Set either `parameters` (external template ref) or inline `params`, not both — the compiler ignores `params` when `parameters` is set, which silently drops author intent. Pick one form per recipe.",
|
|
634
|
+
path: ["params"],
|
|
561
635
|
});
|
|
562
636
|
/**
|
|
563
637
|
* A content-only template. Has fields but no rendering — exists as a data
|
|
@@ -664,11 +738,21 @@ exports.DesignParametersTemplateRecipeSchema = zod_1.z.object({
|
|
|
664
738
|
/** Defaults to "Office/32x32/document.png" if omitted. */
|
|
665
739
|
icon: zod_1.z.string().optional(),
|
|
666
740
|
/**
|
|
667
|
-
*
|
|
668
|
-
*
|
|
741
|
+
* Reference to a `ComponentSectionRecipe` whose section folders this
|
|
742
|
+
* parameters template lands under —
|
|
743
|
+
* `Components/<section.name>/Presentation Parameters/<name>`. Required:
|
|
669
744
|
* presentation parameters are organised per-section by convention.
|
|
745
|
+
*
|
|
746
|
+
* Compile errors INPUT_INVALID if `section.handle` doesn't resolve to
|
|
747
|
+
* a `ComponentSectionRecipe` in the same recipe set. Matches the
|
|
748
|
+
* shape used by `ComponentTemplateRecipe.section` — `{ handle }` ref,
|
|
749
|
+
* not a bare section name string.
|
|
670
750
|
*/
|
|
671
|
-
section: zod_1.z.
|
|
751
|
+
section: zod_1.z.object({
|
|
752
|
+
handle: zod_1.z.string().regex(HANDLE_PATTERN, {
|
|
753
|
+
message: "section.handle must match `<kebab-name>@<major>`",
|
|
754
|
+
}),
|
|
755
|
+
}),
|
|
672
756
|
params: zod_1.z.array(exports.DesignParameterSchema).default([]),
|
|
673
757
|
});
|
|
674
758
|
/**
|
|
@@ -1486,7 +1570,7 @@ exports.EnumerationRecipeSchema = zod_1.z.object({
|
|
|
1486
1570
|
location: zod_1.z
|
|
1487
1571
|
.object({
|
|
1488
1572
|
scope: zod_1.z.enum(["site", "siteCollection"]),
|
|
1489
|
-
folder:
|
|
1573
|
+
folder: FolderPath.optional(),
|
|
1490
1574
|
})
|
|
1491
1575
|
.optional(),
|
|
1492
1576
|
values: zod_1.z.array(exports.EnumerationValueSchema).min(1),
|
|
@@ -45,3 +45,23 @@ export declare const renderSourceFields: (fields: SourceFields, resolveHandle: (
|
|
|
45
45
|
* `ref-source-fields`.
|
|
46
46
|
*/
|
|
47
47
|
export declare const sourceFieldsNeedHandleResolution: (fields: SourceFields) => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Convert the author-surface `SitecoreFieldSource` discriminated union
|
|
50
|
+
* to the flat `SourceFields` bag the renderer + IR consume. Internal
|
|
51
|
+
* adapter — author surface stays union-shaped (clean for JSON Schema
|
|
52
|
+
* and Agent Studio), compiler internals stay flat-shaped (clean for
|
|
53
|
+
* `renderSourceFields` and the `ref-source-fields` IR op).
|
|
54
|
+
*
|
|
55
|
+
* Returns an empty bag when `source` is undefined, so callers can
|
|
56
|
+
* unconditionally invoke this and feed the result to
|
|
57
|
+
* `renderSourceFields` / `sourceFieldsNeedHandleResolution`.
|
|
58
|
+
*/
|
|
59
|
+
export declare const augmentSourceToFields: (source: {
|
|
60
|
+
kind: "filter";
|
|
61
|
+
types?: readonly string[];
|
|
62
|
+
query?: string;
|
|
63
|
+
scope?: string;
|
|
64
|
+
} | {
|
|
65
|
+
kind: "raw";
|
|
66
|
+
value: string;
|
|
67
|
+
} | undefined) => SourceFields;
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* `/sitecore/content/Tags` Treelist source) use `sourceRaw`.
|
|
26
26
|
*/
|
|
27
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.sourceFieldsNeedHandleResolution = exports.renderSourceFields = void 0;
|
|
28
|
+
exports.augmentSourceToFields = exports.sourceFieldsNeedHandleResolution = exports.renderSourceFields = void 0;
|
|
29
29
|
const formatGuidCurly = (guid) => `{${guid.toUpperCase()}}`;
|
|
30
30
|
/**
|
|
31
31
|
* Compose structured source fields into the Sitecore-encoded Source string.
|
|
@@ -77,3 +77,27 @@ exports.renderSourceFields = renderSourceFields;
|
|
|
77
77
|
*/
|
|
78
78
|
const sourceFieldsNeedHandleResolution = (fields) => Array.isArray(fields.sourceTypes) && fields.sourceTypes.length > 0;
|
|
79
79
|
exports.sourceFieldsNeedHandleResolution = sourceFieldsNeedHandleResolution;
|
|
80
|
+
/**
|
|
81
|
+
* Convert the author-surface `SitecoreFieldSource` discriminated union
|
|
82
|
+
* to the flat `SourceFields` bag the renderer + IR consume. Internal
|
|
83
|
+
* adapter — author surface stays union-shaped (clean for JSON Schema
|
|
84
|
+
* and Agent Studio), compiler internals stay flat-shaped (clean for
|
|
85
|
+
* `renderSourceFields` and the `ref-source-fields` IR op).
|
|
86
|
+
*
|
|
87
|
+
* Returns an empty bag when `source` is undefined, so callers can
|
|
88
|
+
* unconditionally invoke this and feed the result to
|
|
89
|
+
* `renderSourceFields` / `sourceFieldsNeedHandleResolution`.
|
|
90
|
+
*/
|
|
91
|
+
const augmentSourceToFields = (source) => {
|
|
92
|
+
if (!source)
|
|
93
|
+
return {};
|
|
94
|
+
if (source.kind === "raw") {
|
|
95
|
+
return { sourceRaw: source.value };
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
sourceTypes: source.types,
|
|
99
|
+
sourceQuery: source.query,
|
|
100
|
+
sourceScope: source.scope,
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
exports.augmentSourceToFields = augmentSourceToFields;
|
|
@@ -10,8 +10,8 @@ import type { Recipe } from "./schema/recipe";
|
|
|
10
10
|
* Reference inventory checked:
|
|
11
11
|
*
|
|
12
12
|
* ComponentTemplateRecipe / ContentTemplateRecipe
|
|
13
|
-
* fields[*].sitecore.
|
|
14
|
-
* params[*].sitecore.
|
|
13
|
+
* fields[*].sitecore.source.types[*] → any template-bearing recipe
|
|
14
|
+
* params[*].sitecore.source.types[*] → any template-bearing recipe
|
|
15
15
|
* insertOptions[*] → any template-bearing recipe
|
|
16
16
|
*
|
|
17
17
|
* ComponentTemplateRecipe
|
|
@@ -23,7 +23,7 @@ import type { Recipe } from "./schema/recipe";
|
|
|
23
23
|
* fields[*].reference.refs[*] → any recipe
|
|
24
24
|
*
|
|
25
25
|
* PageTemplateRecipe
|
|
26
|
-
* fields[*].sitecore.
|
|
26
|
+
* fields[*].sitecore.source.types[*] → any template-bearing recipe
|
|
27
27
|
* insertOptions[*] → PageTemplateRecipe
|
|
28
28
|
* layout.placeholders[*][*].componentHandle → ComponentTemplateRecipe
|
|
29
29
|
* layout.placeholders[*][*].datasourceRef.handle → ContentItemRecipe
|
package/dist/recipe/validate.js
CHANGED
|
@@ -5,6 +5,16 @@ exports.formatValidationErrors = formatValidationErrors;
|
|
|
5
5
|
exports.validateRecipeSet = validateRecipeSet;
|
|
6
6
|
exports.validateRecipeSetOrThrow = validateRecipeSetOrThrow;
|
|
7
7
|
const errors_1 = require("../shared/errors");
|
|
8
|
+
/**
|
|
9
|
+
* The picker-scope handles to validate on a `SitecoreFieldAugment`.
|
|
10
|
+
* Empty when the augment is absent OR uses the `raw` source mode
|
|
11
|
+
* (verbatim Source string; nothing to resolve).
|
|
12
|
+
*/
|
|
13
|
+
const sourceTypesOf = (augment) => {
|
|
14
|
+
if (augment?.source?.kind !== "filter")
|
|
15
|
+
return [];
|
|
16
|
+
return augment.source.types ?? [];
|
|
17
|
+
};
|
|
8
18
|
const TEMPLATE_KINDS = [
|
|
9
19
|
"component-template",
|
|
10
20
|
"content-template",
|
|
@@ -184,13 +194,13 @@ function validateRecipeSet(recipes) {
|
|
|
184
194
|
switch (recipe.kind) {
|
|
185
195
|
case "component-template":
|
|
186
196
|
recipe.fields.forEach((field, idx) => {
|
|
187
|
-
field.sitecore
|
|
188
|
-
checkRef(recipe.handle, `fields.${idx}.sitecore.
|
|
197
|
+
sourceTypesOf(field.sitecore).forEach((handle, sIdx) => {
|
|
198
|
+
checkRef(recipe.handle, `fields.${idx}.sitecore.source.types.${sIdx}`, handle, TEMPLATE_KINDS);
|
|
189
199
|
});
|
|
190
200
|
});
|
|
191
201
|
recipe.params.forEach((param, idx) => {
|
|
192
|
-
param.sitecore
|
|
193
|
-
checkRef(recipe.handle, `params.${idx}.sitecore.
|
|
202
|
+
sourceTypesOf(param.sitecore).forEach((handle, sIdx) => {
|
|
203
|
+
checkRef(recipe.handle, `params.${idx}.sitecore.source.types.${sIdx}`, handle, TEMPLATE_KINDS);
|
|
194
204
|
});
|
|
195
205
|
});
|
|
196
206
|
recipe.insertOptions?.forEach((handle, idx) => {
|
|
@@ -216,8 +226,8 @@ function validateRecipeSet(recipes) {
|
|
|
216
226
|
break;
|
|
217
227
|
case "design-parameters-template":
|
|
218
228
|
recipe.params.forEach((param, idx) => {
|
|
219
|
-
param.sitecore
|
|
220
|
-
checkRef(recipe.handle, `params.${idx}.sitecore.
|
|
229
|
+
sourceTypesOf(param.sitecore).forEach((handle, sIdx) => {
|
|
230
|
+
checkRef(recipe.handle, `params.${idx}.sitecore.source.types.${sIdx}`, handle, TEMPLATE_KINDS);
|
|
221
231
|
});
|
|
222
232
|
});
|
|
223
233
|
break;
|
|
@@ -227,8 +237,8 @@ function validateRecipeSet(recipes) {
|
|
|
227
237
|
break;
|
|
228
238
|
case "content-template":
|
|
229
239
|
recipe.fields.forEach((field, idx) => {
|
|
230
|
-
field.sitecore
|
|
231
|
-
checkRef(recipe.handle, `fields.${idx}.sitecore.
|
|
240
|
+
sourceTypesOf(field.sitecore).forEach((handle, sIdx) => {
|
|
241
|
+
checkRef(recipe.handle, `fields.${idx}.sitecore.source.types.${sIdx}`, handle, TEMPLATE_KINDS);
|
|
232
242
|
});
|
|
233
243
|
});
|
|
234
244
|
recipe.insertOptions?.forEach((handle, idx) => {
|
|
@@ -280,8 +290,8 @@ function validateRecipeSet(recipes) {
|
|
|
280
290
|
break;
|
|
281
291
|
case "page-template":
|
|
282
292
|
(recipe.fields ?? []).forEach((field, idx) => {
|
|
283
|
-
field.sitecore
|
|
284
|
-
checkRef(recipe.handle, `fields.${idx}.sitecore.
|
|
293
|
+
sourceTypesOf(field.sitecore).forEach((handle, sIdx) => {
|
|
294
|
+
checkRef(recipe.handle, `fields.${idx}.sitecore.source.types.${sIdx}`, handle, TEMPLATE_KINDS);
|
|
285
295
|
});
|
|
286
296
|
});
|
|
287
297
|
recipe.insertOptions?.forEach((handle, idx) => {
|
package/dist/sync/aggregate.js
CHANGED
|
@@ -102,7 +102,7 @@ const aggregateStatus = async (kinds, ctx, options = {}) => {
|
|
|
102
102
|
}
|
|
103
103
|
const items = [];
|
|
104
104
|
for (const file of files) {
|
|
105
|
-
const recipe = (0, io_1.loadRecipe)(file, kind.schema);
|
|
105
|
+
const recipe = await (0, io_1.loadRecipe)(file, kind.schema);
|
|
106
106
|
const id = recipeId(recipe);
|
|
107
107
|
try {
|
|
108
108
|
const plan = await (0, engine_1.syncDiff)(kind, recipe, { kind: kind.name, id }, ctx);
|
|
@@ -139,7 +139,7 @@ const aggregatePush = async (kinds, ctx, options) => {
|
|
|
139
139
|
}
|
|
140
140
|
const items = [];
|
|
141
141
|
for (const file of files) {
|
|
142
|
-
const recipe = (0, io_1.loadRecipe)(file, kind.schema);
|
|
142
|
+
const recipe = await (0, io_1.loadRecipe)(file, kind.schema);
|
|
143
143
|
const id = recipeId(recipe);
|
|
144
144
|
try {
|
|
145
145
|
const outcome = await (0, engine_1.syncPush)(kind, recipe, { kind: kind.name, id }, ctx, {
|
package/dist/sync/io.d.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import type { ZodType } from "zod";
|
|
2
2
|
/**
|
|
3
|
-
* Read, parse, and schema-validate a recipe file.
|
|
4
|
-
*
|
|
3
|
+
* Read, parse, and schema-validate a recipe file. The on-disk format is
|
|
4
|
+
* picked from the file extension:
|
|
5
|
+
*
|
|
6
|
+
* - `.ts` / `.tsx` / `.mts` / `.cts` → transpiled + executed in the
|
|
7
|
+
* recipe sandbox; the default export (or first named export) is
|
|
8
|
+
* Zod-parsed.
|
|
9
|
+
* - everything else → read as text and run through the YAML parser
|
|
10
|
+
* (which also accepts JSON).
|
|
11
|
+
*
|
|
12
|
+
* Async because the TypeScript path forks a child process. All current
|
|
13
|
+
* call sites already run inside `async` task runners or commander
|
|
14
|
+
* `command.action(async …)` handlers.
|
|
5
15
|
*/
|
|
6
|
-
export declare const loadRecipe: <T>(filePath: string, schema: ZodType<T>) => T
|
|
16
|
+
export declare const loadRecipe: <T>(filePath: string, schema: ZodType<T>) => Promise<T>;
|
|
7
17
|
/** Serialize a recipe to a file — JSON when the path ends `.json`, else YAML. */
|
|
8
18
|
export declare const writeRecipe: (filePath: string, recipe: unknown) => void;
|
package/dist/sync/io.js
CHANGED
|
@@ -2,21 +2,56 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.writeRecipe = exports.loadRecipe = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* Recipe file I/O for the `sync` engine — load a recipe from a YAML
|
|
6
|
-
* JSON file and validate it against a kind's schema, or
|
|
7
|
-
* captured recipe back to disk.
|
|
5
|
+
* Recipe file I/O for the `sync` engine — load a recipe from a YAML,
|
|
6
|
+
* JSON, or TypeScript file and validate it against a kind's schema, or
|
|
7
|
+
* serialize a captured recipe back to disk.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* Three on-disk formats are supported, all kinds, one loader:
|
|
10
|
+
*
|
|
11
|
+
* - `.ts` / `.tsx` / `.mts` / `.cts` — TypeScript source. Loaded via
|
|
12
|
+
* the sandboxed transpile path (`@/sync/typescript-recipe`) so a
|
|
13
|
+
* hostile authored recipe cannot run with scai's privileges.
|
|
14
|
+
* Authors get Zod-derived `satisfies` checks at write time.
|
|
15
|
+
* - `.yaml` / `.yml` — YAML.
|
|
16
|
+
* - `.json` — JSON (parsed through the YAML parser; YAML is a superset).
|
|
17
|
+
*
|
|
18
|
+
* See docs/recipe-sync-architecture.md and docs/recipe-sandbox.md.
|
|
10
19
|
*/
|
|
11
20
|
const node_fs_1 = require("node:fs");
|
|
12
21
|
const node_path_1 = require("node:path");
|
|
13
22
|
const yaml_1 = require("yaml");
|
|
14
23
|
const errors_1 = require("../shared/errors");
|
|
24
|
+
const typescript_recipe_1 = require("./typescript-recipe");
|
|
15
25
|
/**
|
|
16
|
-
* Read, parse, and schema-validate a recipe file.
|
|
17
|
-
*
|
|
26
|
+
* Read, parse, and schema-validate a recipe file. The on-disk format is
|
|
27
|
+
* picked from the file extension:
|
|
28
|
+
*
|
|
29
|
+
* - `.ts` / `.tsx` / `.mts` / `.cts` → transpiled + executed in the
|
|
30
|
+
* recipe sandbox; the default export (or first named export) is
|
|
31
|
+
* Zod-parsed.
|
|
32
|
+
* - everything else → read as text and run through the YAML parser
|
|
33
|
+
* (which also accepts JSON).
|
|
34
|
+
*
|
|
35
|
+
* Async because the TypeScript path forks a child process. All current
|
|
36
|
+
* call sites already run inside `async` task runners or commander
|
|
37
|
+
* `command.action(async …)` handlers.
|
|
18
38
|
*/
|
|
19
|
-
const loadRecipe = (filePath, schema) => {
|
|
39
|
+
const loadRecipe = async (filePath, schema) => {
|
|
40
|
+
const parsed = (0, typescript_recipe_1.isTypeScriptRecipePath)(filePath)
|
|
41
|
+
? await (0, typescript_recipe_1.loadTypeScriptRecipe)(filePath)
|
|
42
|
+
: parseYamlOrJsonRecipe(filePath);
|
|
43
|
+
const result = schema.safeParse(parsed);
|
|
44
|
+
if (!result.success) {
|
|
45
|
+
throw (0, errors_1.createScaiError)(`Recipe file "${filePath}" failed schema validation`, "INPUT_INVALID", {
|
|
46
|
+
details: result.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return result.data;
|
|
50
|
+
};
|
|
51
|
+
exports.loadRecipe = loadRecipe;
|
|
52
|
+
/** Read and YAML-parse a recipe file. YAML is a superset of JSON, so
|
|
53
|
+
* `.yaml`, `.yml`, and `.json` all go through the same path. */
|
|
54
|
+
const parseYamlOrJsonRecipe = (filePath) => {
|
|
20
55
|
let raw;
|
|
21
56
|
try {
|
|
22
57
|
raw = (0, node_fs_1.readFileSync)(filePath, "utf8");
|
|
@@ -26,24 +61,15 @@ const loadRecipe = (filePath, schema) => {
|
|
|
26
61
|
hint: error instanceof Error ? error.message : undefined,
|
|
27
62
|
});
|
|
28
63
|
}
|
|
29
|
-
let parsed;
|
|
30
64
|
try {
|
|
31
|
-
|
|
65
|
+
return (0, yaml_1.parse)(raw);
|
|
32
66
|
}
|
|
33
67
|
catch (error) {
|
|
34
68
|
throw (0, errors_1.createScaiError)(`Recipe file "${filePath}" is not valid YAML/JSON`, "INPUT_INVALID", {
|
|
35
69
|
hint: error instanceof Error ? error.message : undefined,
|
|
36
70
|
});
|
|
37
71
|
}
|
|
38
|
-
const result = schema.safeParse(parsed);
|
|
39
|
-
if (!result.success) {
|
|
40
|
-
throw (0, errors_1.createScaiError)(`Recipe file "${filePath}" failed schema validation`, "INPUT_INVALID", {
|
|
41
|
-
details: result.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`),
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
return result.data;
|
|
45
72
|
};
|
|
46
|
-
exports.loadRecipe = loadRecipe;
|
|
47
73
|
/** Serialize a recipe to a file — JSON when the path ends `.json`, else YAML. */
|
|
48
74
|
const writeRecipe = (filePath, recipe) => {
|
|
49
75
|
const serialized = (0, node_path_1.extname)(filePath) === ".json" ? `${JSON.stringify(recipe, null, 2)}\n` : (0, yaml_1.stringify)(recipe);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `.recipe.ts` loader.
|
|
3
|
+
*
|
|
4
|
+
* Authored recipes (CMS, brand-kit, agents, campaigns, briefs, etc.) all
|
|
5
|
+
* load through this path so end users can write `.recipe.ts` files with
|
|
6
|
+
* Zod-derived types and `satisfies` checks. The transpile + sandboxed
|
|
7
|
+
* execute machinery is owned by `@/recipe/sandbox` — this module is the
|
|
8
|
+
* thin wrapper that picks between the confined child and the legacy
|
|
9
|
+
* in-process tsx loader, and is consumed by both:
|
|
10
|
+
*
|
|
11
|
+
* - `src/sync/io.ts:loadRecipe` (schema-aware loader used by brand,
|
|
12
|
+
* agents, campaigns, briefs, …)
|
|
13
|
+
* - `src/recipe/io.ts:loadRecipe` (CMS recipe discriminated-union loader)
|
|
14
|
+
*
|
|
15
|
+
* Output is unvalidated — callers Zod-parse the returned value. See
|
|
16
|
+
* docs/recipe-sandbox.md.
|
|
17
|
+
*/
|
|
18
|
+
/** Whether the given file path should be loaded via the TypeScript path. */
|
|
19
|
+
export declare const isTypeScriptRecipePath: (filePath: string) => boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Load a `.recipe.ts` (or `.tsx`/`.mts`/`.cts`) file and return its
|
|
22
|
+
* exported recipe value. By default the file is loaded in a confined
|
|
23
|
+
* child process (see docs/recipe-sandbox.md) so a hostile recipe cannot
|
|
24
|
+
* run with scai's full privileges. `SITECOREAI_RECIPE_SANDBOX=0` forces
|
|
25
|
+
* the legacy in-process loader (useful for debugging — the warning lands
|
|
26
|
+
* on stderr so JSON-mode CLI streams are not corrupted).
|
|
27
|
+
*
|
|
28
|
+
* The result is unvalidated: callers Zod-parse it.
|
|
29
|
+
*/
|
|
30
|
+
export declare const loadTypeScriptRecipe: (filePath: string) => Promise<unknown>;
|