@zitadel/config 0.1.0-alpha.17 → 0.1.0-alpha.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/defaults/README-branding.md +96 -0
- package/defaults/README-flows.md +21 -4
- package/defaults/README-schemas.md +2 -2
- package/defaults/branding/centered/login.liquid +164 -0
- package/defaults/branding/hero/login.liquid +224 -0
- package/defaults/branding/minimal/login.liquid +154 -0
- package/defaults/branding/split/login.liquid +183 -0
- package/defaults/branding/split-right/login.liquid +183 -0
- package/defaults/default-human-user.json +2 -19
- package/defaults/default-login.json +21 -4
- package/defaults/presets/passkey-first/human-user.json +2 -19
- package/defaults/presets/passkey-first/login.json +31 -4
- package/dist/branding-url.d.mts +14 -0
- package/dist/branding-url.d.mts.map +1 -0
- package/dist/branding-url.mjs +23 -0
- package/dist/branding-url.mjs.map +1 -0
- package/dist/defaults-Bh4_xVbh.d.mts +84 -0
- package/dist/defaults-Bh4_xVbh.d.mts.map +1 -0
- package/dist/defaults-eiGHyI5Y.mjs +569 -0
- package/dist/defaults-eiGHyI5Y.mjs.map +1 -0
- package/dist/defaults.d.mts +2 -2
- package/dist/defaults.mjs +2 -2
- package/dist/index.d.mts +6 -4
- package/dist/index.mjs +6 -4
- package/dist/{meta-schemas-8nlyVL7g.mjs → meta-schemas-CSc18JqO.mjs} +92 -31
- package/dist/meta-schemas-CSc18JqO.mjs.map +1 -0
- package/dist/meta-schemas.d.mts +6 -1
- package/dist/meta-schemas.d.mts.map +1 -1
- package/dist/meta-schemas.mjs +2 -2
- package/dist/normalize.d.mts +11 -9
- package/dist/normalize.d.mts.map +1 -1
- package/dist/normalize.mjs +24 -17
- package/dist/normalize.mjs.map +1 -1
- package/dist/schemas.d.mts +14 -1
- package/dist/schemas.d.mts.map +1 -1
- package/dist/schemas.mjs +62 -2
- package/dist/schemas.mjs.map +1 -1
- package/dist/template.d.mts +76 -0
- package/dist/template.d.mts.map +1 -0
- package/dist/template.mjs +145 -0
- package/dist/template.mjs.map +1 -0
- package/dist/validate.mjs +70 -11
- package/dist/validate.mjs.map +1 -1
- package/meta-schemas/auth-method.json +2 -7
- package/meta-schemas/branding.json +44 -0
- package/meta-schemas/flow-definition.json +20 -1
- package/meta-schemas/property-name.json +7 -0
- package/meta-schemas/user-property.json +8 -24
- package/meta-schemas/user-schema.json +4 -1
- package/package.json +17 -4
- package/dist/defaults-BQej3KIY.mjs +0 -388
- package/dist/defaults-BQej3KIY.mjs.map +0 -1
- package/dist/defaults-CSjBjBkQ.d.mts +0 -41
- package/dist/defaults-CSjBjBkQ.d.mts.map +0 -1
- package/dist/meta-schemas-8nlyVL7g.mjs.map +0 -1
|
@@ -3,18 +3,12 @@ var auth_method_default = {
|
|
|
3
3
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
title: "AuthMethod",
|
|
5
5
|
type: "object",
|
|
6
|
-
required: ["enabled"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"position": {
|
|
13
|
-
"type": "integer",
|
|
14
|
-
"minimum": 0,
|
|
15
|
-
"description": "The position of the authentication method in the list of supported methods"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
6
|
+
required: ["enabled"],
|
|
7
|
+
additionalProperties: false,
|
|
8
|
+
properties: { "enabled": {
|
|
9
|
+
"type": "boolean",
|
|
10
|
+
"description": "Whether the authentication method is enabled or not"
|
|
11
|
+
} }
|
|
18
12
|
};
|
|
19
13
|
//#endregion
|
|
20
14
|
//#region meta-schemas/auth-methods.json
|
|
@@ -33,6 +27,50 @@ var auth_methods_default = {
|
|
|
33
27
|
}
|
|
34
28
|
};
|
|
35
29
|
//#endregion
|
|
30
|
+
//#region meta-schemas/branding.json
|
|
31
|
+
var branding_default = {
|
|
32
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
33
|
+
title: "Branding",
|
|
34
|
+
description: "Local branding descriptor for the login UI. Carries the layout preset, asset URLs, and the LiquidJS template (by file reference or inline). `zitadel apply` publishes this as an immutable branding revision; flow responses resolve the latest revision per project (ADR 040). This schema describes the local configuration shape only.",
|
|
35
|
+
type: "object",
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
properties: {
|
|
38
|
+
"$schema": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Editor affordance: path or URL of this meta-schema so editors validate and autocomplete the file (scaffolded branding files point at `.zitadel/meta/branding.json`). Ignored by the platform."
|
|
41
|
+
},
|
|
42
|
+
"layout": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"enum": ["centered", "split"],
|
|
45
|
+
"default": "centered",
|
|
46
|
+
"description": "Degrade preset (`centered` or `split`) the bundled default template branches on, not the complete design catalog. All named designs (`centered`, `split`, `split-right`, `hero`, `minimal`) ship as templates and map onto one of these two values; switch designs with the `branding eject --design <name>` command instead of editing this field. Custom templates may ignore it; the preset is also used when a template fails component-side validation."
|
|
47
|
+
},
|
|
48
|
+
"liquid_template_file": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"examples": ["./login.liquid"],
|
|
51
|
+
"description": "Path to the LiquidJS template file, relative to this descriptor. The CLI inlines the file content into `liquid_template` when publishing, and writes server state back to the file. Mutually exclusive with `liquid_template`."
|
|
52
|
+
},
|
|
53
|
+
"liquid_template": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"maxLength": 131072,
|
|
56
|
+
"description": "Inline LiquidJS template string. Prefer `liquid_template_file` — a real `.liquid` file edits far better than a JSON-escaped string. Mutually exclusive with `liquid_template_file`."
|
|
57
|
+
},
|
|
58
|
+
"logo_url": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"format": "uri",
|
|
61
|
+
"pattern": "^([hH][tT][tT][pP][sS]://|[hH][tT][tT][pP]://([lL][oO][cC][aA][lL][hH][oO][sS][tT]|127\\.(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\\.){2}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])|\\[::1\\])(?::0*(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?([/?#]|$))",
|
|
62
|
+
"description": "Logo URL (https required; canonical loopback http is allowed for local development and renders only on loopback pages)."
|
|
63
|
+
},
|
|
64
|
+
"hero_url": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"format": "uri",
|
|
67
|
+
"pattern": "^([hH][tT][tT][pP][sS]://|[hH][tT][tT][pP]://([lL][oO][cC][aA][lL][hH][oO][sS][tT]|127\\.(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\\.){2}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])|\\[::1\\])(?::0*(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?([/?#]|$))",
|
|
68
|
+
"description": "Hero/background image URL (https required; canonical loopback http is allowed for local development and renders only on loopback pages), used by split designs."
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
not: { "required": ["liquid_template", "liquid_template_file"] }
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
36
74
|
//#region meta-schemas/flow-definition.json
|
|
37
75
|
var flow_definition_default = {
|
|
38
76
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -297,12 +335,34 @@ var flow_definition_default = {
|
|
|
297
335
|
null
|
|
298
336
|
],
|
|
299
337
|
"description": "How to interpret `target`. - Omitted: transition to a step in the current definition. - `switch`: replace the current flow with the target flow. No return; the current flow is discarded. Typical use: navigating between peer flows like login ↔ register. - `pivot`: push the target flow onto the stack. The current flow is paused; when the target flow completes the engine auto-pops back. Typical use: login → recovery → back to login."
|
|
338
|
+
},
|
|
339
|
+
"purpose": {
|
|
340
|
+
"type": ["string", "null"],
|
|
341
|
+
"enum": [
|
|
342
|
+
"login",
|
|
343
|
+
"register",
|
|
344
|
+
"recovery",
|
|
345
|
+
"profiling",
|
|
346
|
+
"reauth",
|
|
347
|
+
"link_account",
|
|
348
|
+
null
|
|
349
|
+
],
|
|
350
|
+
"description": "Local re-purposing. When set, taking this transition changes the flow's current purpose to this value — the dispatch mode a step's challenges run under — while the flow's original purpose stays pinned. Must be a purpose this definition serves, and `target` must be that purpose's entry step. Mutually exclusive with `action`: a transition either re-purposes within this flow or targets another flow, never both. Example: a \"Sign up\" navigation on the login identifier step (`{ \"target\": \"register\", \"purpose\": \"register\" }`)."
|
|
300
351
|
}
|
|
301
352
|
}
|
|
302
353
|
}
|
|
303
354
|
}
|
|
304
355
|
};
|
|
305
356
|
//#endregion
|
|
357
|
+
//#region meta-schemas/property-name.json
|
|
358
|
+
var property_name_default = {
|
|
359
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
360
|
+
title: "PropertyName",
|
|
361
|
+
type: "string",
|
|
362
|
+
pattern: "^[^.]+$",
|
|
363
|
+
description: "The name of a single user attribute."
|
|
364
|
+
};
|
|
365
|
+
//#endregion
|
|
306
366
|
//#region meta-schemas/user-property.json
|
|
307
367
|
var user_property_default = {
|
|
308
368
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -316,10 +376,6 @@ var user_property_default = {
|
|
|
316
376
|
"const": "https://json-schema.org/draft/2020-12/schema",
|
|
317
377
|
"description": "The JSON Schema version used for this schema."
|
|
318
378
|
},
|
|
319
|
-
"x-verify": {
|
|
320
|
-
"oneOf": [{ "type": "string" }, { "type": "null" }],
|
|
321
|
-
"description": "The verification method for this property, if applicable"
|
|
322
|
-
},
|
|
323
379
|
"x-unique": {
|
|
324
380
|
"oneOf": [{
|
|
325
381
|
"type": "string",
|
|
@@ -335,25 +391,16 @@ var user_property_default = {
|
|
|
335
391
|
"oneOf": [{ "type": "string" }, { "type": "null" }],
|
|
336
392
|
"description": "The claim name for this property, if applicable"
|
|
337
393
|
},
|
|
338
|
-
"x-
|
|
339
|
-
"type": "boolean",
|
|
340
|
-
"default": true,
|
|
341
|
-
"description": "Whether this property is editable by the user or not"
|
|
342
|
-
},
|
|
343
|
-
"x-sensitive": {
|
|
344
|
-
"type": "boolean",
|
|
345
|
-
"default": false,
|
|
346
|
-
"description": "Whether this property contains sensitive information or not"
|
|
347
|
-
},
|
|
348
|
-
"x-mfa": {
|
|
394
|
+
"x-audit": {
|
|
349
395
|
"type": "boolean",
|
|
350
396
|
"default": false,
|
|
351
|
-
"description": "Whether this property
|
|
397
|
+
"description": "Whether this property's value may appear in audit event payloads. Audit payloads are deny-by-default: a property without this annotation contributes its key but never its value."
|
|
352
398
|
},
|
|
353
399
|
"properties": {
|
|
354
400
|
"type": "object",
|
|
355
401
|
"description": "A map of additional properties for the user definition, where the key is the property name and the value is the property schema",
|
|
356
|
-
"
|
|
402
|
+
"propertyNames": { "$ref": "property-name.json" },
|
|
403
|
+
"additionalProperties": { "$ref": "user-property.json" }
|
|
357
404
|
}
|
|
358
405
|
}
|
|
359
406
|
};
|
|
@@ -397,6 +444,7 @@ var user_schema_default = {
|
|
|
397
444
|
"properties": {
|
|
398
445
|
"type": "object",
|
|
399
446
|
"description": "A map of additional properties for the user definition, where the key is the property name and the value is the property schema",
|
|
447
|
+
"propertyNames": { "$ref": "property-name.json" },
|
|
400
448
|
"additionalProperties": { "$ref": "user-property.json" }
|
|
401
449
|
}
|
|
402
450
|
}
|
|
@@ -422,6 +470,11 @@ const META_SCHEMA_DIR = ".zitadel/meta";
|
|
|
422
470
|
*/
|
|
423
471
|
const FLOW_FILE_SCHEMA_REF = "../meta/flow-definition.json";
|
|
424
472
|
/**
|
|
473
|
+
* The `$schema` value scaffolded branding files carry, relative to
|
|
474
|
+
* `.zitadel/branding/` — resolves to `{@link META_SCHEMA_DIR}/branding.json`.
|
|
475
|
+
*/
|
|
476
|
+
const BRANDING_FILE_SCHEMA_REF = "../meta/branding.json";
|
|
477
|
+
/**
|
|
425
478
|
* The dialect files to materialize, in write order. `auth-methods.json` and
|
|
426
479
|
* `auth-method.json` are pulled in by `user-schema.json`'s relative `$ref`s —
|
|
427
480
|
* without them the copied dialect cannot resolve offline.
|
|
@@ -440,6 +493,10 @@ function metaSchemaFiles() {
|
|
|
440
493
|
name: "user-property.json",
|
|
441
494
|
body: user_property_default
|
|
442
495
|
},
|
|
496
|
+
{
|
|
497
|
+
name: "property-name.json",
|
|
498
|
+
body: property_name_default
|
|
499
|
+
},
|
|
443
500
|
{
|
|
444
501
|
name: "auth-methods.json",
|
|
445
502
|
body: auth_methods_default
|
|
@@ -447,10 +504,14 @@ function metaSchemaFiles() {
|
|
|
447
504
|
{
|
|
448
505
|
name: "auth-method.json",
|
|
449
506
|
body: auth_method_default
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
name: "branding.json",
|
|
510
|
+
body: branding_default
|
|
450
511
|
}
|
|
451
512
|
];
|
|
452
513
|
}
|
|
453
514
|
//#endregion
|
|
454
|
-
export {
|
|
515
|
+
export { metaSchemaFiles as i, FLOW_FILE_SCHEMA_REF as n, META_SCHEMA_DIR as r, BRANDING_FILE_SCHEMA_REF as t };
|
|
455
516
|
|
|
456
|
-
//# sourceMappingURL=meta-schemas-
|
|
517
|
+
//# sourceMappingURL=meta-schemas-CSc18JqO.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-schemas-CSc18JqO.mjs","names":["flowDefinitionMetaSchema","userSchemaMetaSchema","userPropertyMetaSchema","propertyNameMetaSchema","authMethodsMetaSchema","authMethodMetaSchema","brandingMetaSchema"],"sources":["../meta-schemas/auth-method.json","../meta-schemas/auth-methods.json","../meta-schemas/branding.json","../meta-schemas/flow-definition.json","../meta-schemas/property-name.json","../meta-schemas/user-property.json","../meta-schemas/user-schema.json","../src/meta-schemas.ts"],"sourcesContent":["","","","","","","","/**\n * The dialect meta-schemas `zitadel setup` copies into a project's\n * `.zitadel/meta/` so the flow/schema dialect is machine-readable offline:\n * editors validate flow files against `flow-definition.json` via a relative\n * `$schema` pointer, and agents read the dialect files before authoring\n * edits — no docs crawl, no hosted schema required.\n *\n * The files under `meta-schemas/` are committed copies of\n * `api/openapi/endpoints/schemas/*.json` (the source the server embeds);\n * a drift-audit test keeps them byte-identical in the monorepo.\n */\nimport authMethodMetaSchema from \"../meta-schemas/auth-method.json\" with {\n type: \"json\",\n};\nimport authMethodsMetaSchema from \"../meta-schemas/auth-methods.json\" with {\n type: \"json\",\n};\nimport brandingMetaSchema from \"../meta-schemas/branding.json\" with {\n type: \"json\",\n};\nimport flowDefinitionMetaSchema from \"../meta-schemas/flow-definition.json\" with {\n type: \"json\",\n};\nimport propertyNameMetaSchema from \"../meta-schemas/property-name.json\" with {\n type: \"json\",\n};\nimport userPropertyMetaSchema from \"../meta-schemas/user-property.json\" with {\n type: \"json\",\n};\nimport userSchemaMetaSchema from \"../meta-schemas/user-schema.json\" with {\n type: \"json\",\n};\n\n/** Project-relative directory setup writes the meta-schemas to. */\nexport const META_SCHEMA_DIR = \".zitadel/meta\";\n\n/**\n * The `$schema` value scaffolded flow files carry, relative to\n * `.zitadel/flows/` — resolves to `{@link META_SCHEMA_DIR}/flow-definition.json`.\n */\nexport const FLOW_FILE_SCHEMA_REF = \"../meta/flow-definition.json\";\n\n/**\n * The `$schema` value scaffolded branding files carry, relative to\n * `.zitadel/branding/` — resolves to `{@link META_SCHEMA_DIR}/branding.json`.\n */\nexport const BRANDING_FILE_SCHEMA_REF = \"../meta/branding.json\";\n\nexport type MetaSchemaFile = { name: string; body: object };\n\n/**\n * The dialect files to materialize, in write order. `auth-methods.json` and\n * `auth-method.json` are pulled in by `user-schema.json`'s relative `$ref`s —\n * without them the copied dialect cannot resolve offline.\n */\nexport function metaSchemaFiles(): ReadonlyArray<MetaSchemaFile> {\n return [\n { name: \"flow-definition.json\", body: flowDefinitionMetaSchema as object },\n { name: \"user-schema.json\", body: userSchemaMetaSchema as object },\n { name: \"user-property.json\", body: userPropertyMetaSchema as object },\n { name: \"property-name.json\", body: propertyNameMetaSchema as object },\n { name: \"auth-methods.json\", body: authMethodsMetaSchema as object },\n { name: \"auth-method.json\", body: authMethodMetaSchema as object },\n { name: \"branding.json\", body: brandingMetaSchema as object },\n ];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AOkCA,MAAa,kBAAkB;;;;;AAM/B,MAAa,uBAAuB;;;;;AAMpC,MAAa,2BAA2B;;;;;;AASxC,SAAgB,kBAAiD;AAC/D,QAAO;EACL;GAAE,MAAM;GAAwB,MAAMA;GAAoC;EAC1E;GAAE,MAAM;GAAoB,MAAMC;GAAgC;EAClE;GAAE,MAAM;GAAsB,MAAMC;GAAkC;EACtE;GAAE,MAAM;GAAsB,MAAMC;GAAkC;EACtE;GAAE,MAAM;GAAqB,MAAMC;GAAiC;EACpE;GAAE,MAAM;GAAoB,MAAMC;GAAgC;EAClE;GAAE,MAAM;GAAiB,MAAMC;GAA8B;EAC9D"}
|
package/dist/meta-schemas.d.mts
CHANGED
|
@@ -6,6 +6,11 @@ declare const META_SCHEMA_DIR = ".zitadel/meta";
|
|
|
6
6
|
* `.zitadel/flows/` — resolves to `{@link META_SCHEMA_DIR}/flow-definition.json`.
|
|
7
7
|
*/
|
|
8
8
|
declare const FLOW_FILE_SCHEMA_REF = "../meta/flow-definition.json";
|
|
9
|
+
/**
|
|
10
|
+
* The `$schema` value scaffolded branding files carry, relative to
|
|
11
|
+
* `.zitadel/branding/` — resolves to `{@link META_SCHEMA_DIR}/branding.json`.
|
|
12
|
+
*/
|
|
13
|
+
declare const BRANDING_FILE_SCHEMA_REF = "../meta/branding.json";
|
|
9
14
|
type MetaSchemaFile = {
|
|
10
15
|
name: string;
|
|
11
16
|
body: object;
|
|
@@ -17,5 +22,5 @@ type MetaSchemaFile = {
|
|
|
17
22
|
*/
|
|
18
23
|
declare function metaSchemaFiles(): ReadonlyArray<MetaSchemaFile>;
|
|
19
24
|
//#endregion
|
|
20
|
-
export { FLOW_FILE_SCHEMA_REF, META_SCHEMA_DIR, MetaSchemaFile, metaSchemaFiles };
|
|
25
|
+
export { BRANDING_FILE_SCHEMA_REF, FLOW_FILE_SCHEMA_REF, META_SCHEMA_DIR, MetaSchemaFile, metaSchemaFiles };
|
|
21
26
|
//# sourceMappingURL=meta-schemas.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meta-schemas.d.mts","names":[],"sources":["../src/meta-schemas.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"meta-schemas.d.mts","names":[],"sources":["../src/meta-schemas.ts"],"mappings":";;cAkCa,eAAA;;;;;cAMA,oBAAA;;;;;cAMA,wBAAA;AAAA,KAED,cAAA;EAAmB,IAAA;EAAc,IAAA;AAAA;AAA7C;;;;;AAAA,iBAOgB,eAAA,CAAA,GAAmB,aAAA,CAAc,cAAA"}
|
package/dist/meta-schemas.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
export { FLOW_FILE_SCHEMA_REF, META_SCHEMA_DIR, metaSchemaFiles };
|
|
1
|
+
import { i as metaSchemaFiles, n as FLOW_FILE_SCHEMA_REF, r as META_SCHEMA_DIR, t as BRANDING_FILE_SCHEMA_REF } from "./meta-schemas-CSc18JqO.mjs";
|
|
2
|
+
export { BRANDING_FILE_SCHEMA_REF, FLOW_FILE_SCHEMA_REF, META_SCHEMA_DIR, metaSchemaFiles };
|
package/dist/normalize.d.mts
CHANGED
|
@@ -5,21 +5,22 @@
|
|
|
5
5
|
* The sync engine compares `.zitadel/**` files against server responses;
|
|
6
6
|
* the server echoes fields the author never wrote (an empty `audience`
|
|
7
7
|
* on flows) and the meta-schema declares defaults an author may or may
|
|
8
|
-
* not spell out (`x-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* not spell out (`x-audit` on schema properties). Normalizing both
|
|
9
|
+
* sides before hashing or diffing keeps a one-field edit rendering as a
|
|
10
|
+
* one-field diff.
|
|
11
11
|
*
|
|
12
12
|
* Normalized bodies are for COMPARISON. Never upload them, and never
|
|
13
13
|
* write a normalized schema body to a file: the server stores schema
|
|
14
14
|
* bytes verbatim without materializing meta-schema defaults, so a
|
|
15
|
-
* stripped `"x-
|
|
15
|
+
* stripped `"x-audit": false` would vanish from the next published
|
|
16
16
|
* revision. (Flow write-back may reuse {@link normalizeFlowBody} —
|
|
17
17
|
* everything it strips is pure transport noise.)
|
|
18
18
|
*/
|
|
19
19
|
/**
|
|
20
20
|
* Property-level defaults declared by the user-property meta-schema
|
|
21
21
|
* (`api/openapi/endpoints/schemas/user-property.json`). A property that
|
|
22
|
-
* spells one of these out is semantically identical to one that omits it
|
|
22
|
+
* spells one of these out is semantically identical to one that omits it:
|
|
23
|
+
* audit payloads deny by default.
|
|
23
24
|
*/
|
|
24
25
|
declare const USER_PROPERTY_DEFAULTS: Readonly<Record<string, boolean>>;
|
|
25
26
|
/**
|
|
@@ -34,10 +35,11 @@ declare const USER_PROPERTY_DEFAULTS: Readonly<Record<string, boolean>>;
|
|
|
34
35
|
declare function normalizeFlowBody(body: object): object;
|
|
35
36
|
/**
|
|
36
37
|
* Return a deep copy of a user-schema body with meta-schema property
|
|
37
|
-
* defaults stripped: a property carrying
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* defaults stripped: a property carrying one of the
|
|
39
|
+
* {@link USER_PROPERTY_DEFAULTS} normalizes to one omitting it. Only
|
|
40
|
+
* exact default values are removed; `$id` and every other field pass
|
|
41
|
+
* through untouched. Nested `properties` are descended into, so a leaf
|
|
42
|
+
* of an object property normalizes the same way a top-level one does.
|
|
41
43
|
*/
|
|
42
44
|
declare function normalizeSchemaBody(body: object): object;
|
|
43
45
|
//#endregion
|
package/dist/normalize.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.mts","names":[],"sources":["../src/normalize.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"normalize.d.mts","names":[],"sources":["../src/normalize.ts"],"mappings":";;AAwBA;;;;;AAwCA;;;;;AAoBA;;;;;;;;;;;;cA5Da,sBAAA,EAAwB,QAAA,CAAS,MAAA;;;;;;;;;;iBAwC9B,iBAAA,CAAkB,IAAA;;;;;;;;;iBAoBlB,mBAAA,CAAoB,IAAA"}
|
package/dist/normalize.mjs
CHANGED
|
@@ -5,27 +5,24 @@
|
|
|
5
5
|
* The sync engine compares `.zitadel/**` files against server responses;
|
|
6
6
|
* the server echoes fields the author never wrote (an empty `audience`
|
|
7
7
|
* on flows) and the meta-schema declares defaults an author may or may
|
|
8
|
-
* not spell out (`x-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* not spell out (`x-audit` on schema properties). Normalizing both
|
|
9
|
+
* sides before hashing or diffing keeps a one-field edit rendering as a
|
|
10
|
+
* one-field diff.
|
|
11
11
|
*
|
|
12
12
|
* Normalized bodies are for COMPARISON. Never upload them, and never
|
|
13
13
|
* write a normalized schema body to a file: the server stores schema
|
|
14
14
|
* bytes verbatim without materializing meta-schema defaults, so a
|
|
15
|
-
* stripped `"x-
|
|
15
|
+
* stripped `"x-audit": false` would vanish from the next published
|
|
16
16
|
* revision. (Flow write-back may reuse {@link normalizeFlowBody} —
|
|
17
17
|
* everything it strips is pure transport noise.)
|
|
18
18
|
*/
|
|
19
19
|
/**
|
|
20
20
|
* Property-level defaults declared by the user-property meta-schema
|
|
21
21
|
* (`api/openapi/endpoints/schemas/user-property.json`). A property that
|
|
22
|
-
* spells one of these out is semantically identical to one that omits it
|
|
22
|
+
* spells one of these out is semantically identical to one that omits it:
|
|
23
|
+
* audit payloads deny by default.
|
|
23
24
|
*/
|
|
24
|
-
const USER_PROPERTY_DEFAULTS = {
|
|
25
|
-
"x-editable": true,
|
|
26
|
-
"x-sensitive": false,
|
|
27
|
-
"x-mfa": false
|
|
28
|
-
};
|
|
25
|
+
const USER_PROPERTY_DEFAULTS = { "x-audit": false };
|
|
29
26
|
/**
|
|
30
27
|
* Keys of the flow-definition detail envelope. `fetch` in the flow syncer
|
|
31
28
|
* already unwraps the envelope; stripping them here as well makes the
|
|
@@ -68,19 +65,29 @@ function normalizeFlowBody(body) {
|
|
|
68
65
|
}
|
|
69
66
|
/**
|
|
70
67
|
* Return a deep copy of a user-schema body with meta-schema property
|
|
71
|
-
* defaults stripped: a property carrying
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
68
|
+
* defaults stripped: a property carrying one of the
|
|
69
|
+
* {@link USER_PROPERTY_DEFAULTS} normalizes to one omitting it. Only
|
|
70
|
+
* exact default values are removed; `$id` and every other field pass
|
|
71
|
+
* through untouched. Nested `properties` are descended into, so a leaf
|
|
72
|
+
* of an object property normalizes the same way a top-level one does.
|
|
75
73
|
*/
|
|
76
74
|
function normalizeSchemaBody(body) {
|
|
77
75
|
const result = structuredClone(body);
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
stripPropertyDefaults(result);
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Strip {@link USER_PROPERTY_DEFAULTS} from every property of an object
|
|
81
|
+
* schema, descending into nested `properties` so a leaf of an object
|
|
82
|
+
* property normalizes the same way a top-level one does.
|
|
83
|
+
*/
|
|
84
|
+
function stripPropertyDefaults(schema) {
|
|
85
|
+
if (!isPlainObject(schema.properties)) return;
|
|
86
|
+
for (const property of Object.values(schema.properties)) {
|
|
80
87
|
if (!isPlainObject(property)) continue;
|
|
81
88
|
for (const [key, defaultValue] of Object.entries(USER_PROPERTY_DEFAULTS)) if (property[key] === defaultValue) delete property[key];
|
|
89
|
+
stripPropertyDefaults(property);
|
|
82
90
|
}
|
|
83
|
-
return result;
|
|
84
91
|
}
|
|
85
92
|
//#endregion
|
|
86
93
|
export { USER_PROPERTY_DEFAULTS, normalizeFlowBody, normalizeSchemaBody };
|
package/dist/normalize.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.mjs","names":[],"sources":["../src/normalize.ts"],"sourcesContent":["/**\n * Canonical-form normalizers for the two repo-config resource kinds.\n *\n * The sync engine compares `.zitadel/**` files against server responses;\n * the server echoes fields the author never wrote (an empty `audience`\n * on flows) and the meta-schema declares defaults an author may or may\n * not spell out (`x-
|
|
1
|
+
{"version":3,"file":"normalize.mjs","names":[],"sources":["../src/normalize.ts"],"sourcesContent":["/**\n * Canonical-form normalizers for the two repo-config resource kinds.\n *\n * The sync engine compares `.zitadel/**` files against server responses;\n * the server echoes fields the author never wrote (an empty `audience`\n * on flows) and the meta-schema declares defaults an author may or may\n * not spell out (`x-audit` on schema properties). Normalizing both\n * sides before hashing or diffing keeps a one-field edit rendering as a\n * one-field diff.\n *\n * Normalized bodies are for COMPARISON. Never upload them, and never\n * write a normalized schema body to a file: the server stores schema\n * bytes verbatim without materializing meta-schema defaults, so a\n * stripped `\"x-audit\": false` would vanish from the next published\n * revision. (Flow write-back may reuse {@link normalizeFlowBody} —\n * everything it strips is pure transport noise.)\n */\n\n/**\n * Property-level defaults declared by the user-property meta-schema\n * (`api/openapi/endpoints/schemas/user-property.json`). A property that\n * spells one of these out is semantically identical to one that omits it:\n * audit payloads deny by default.\n */\nexport const USER_PROPERTY_DEFAULTS: Readonly<Record<string, boolean>> = {\n \"x-audit\": false,\n};\n\n/**\n * Keys of the flow-definition detail envelope. `fetch` in the flow syncer\n * already unwraps the envelope; stripping them here as well makes the\n * normalizer safe on raw detail responses.\n */\nconst FLOW_ENVELOPE_KEYS = [\"id\", \"project_id\", \"schema_uri\", \"created_at\", \"updated_at\"];\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * An `audience` counts as empty when it scopes nothing: no keys, or only\n * `team_ids`/`app_ids` that are null or empty arrays. Unknown keys keep\n * the audience — better a noisy diff than a silently dropped scope.\n */\nfunction isEmptyAudience(value: unknown): boolean {\n if (!isPlainObject(value)) {\n return false;\n }\n return Object.entries(value).every(\n ([key, entry]) =>\n (key === \"team_ids\" || key === \"app_ids\") &&\n (entry === null || entry === undefined || (Array.isArray(entry) && entry.length === 0)),\n );\n}\n\n/**\n * Return a deep copy of a bare flow-definition body with server-echoed\n * noise removed: an empty `audience` and any detail-envelope keys. A\n * top-level `$schema` is stripped too — it is a local editor affordance\n * (scaffolded flow files point it at `.zitadel/meta/flow-definition.json`),\n * never part of the resource: the server skips it on decode and omits it\n * from responses, so comparing with it would read every scaffolded file\n * as permanently diverged.\n */\nexport function normalizeFlowBody(body: object): object {\n const result = structuredClone(body) as Record<string, unknown>;\n for (const key of FLOW_ENVELOPE_KEYS) {\n delete result[key];\n }\n delete result.$schema;\n if (\"audience\" in result && isEmptyAudience(result.audience)) {\n delete result.audience;\n }\n return result;\n}\n\n/**\n * Return a deep copy of a user-schema body with meta-schema property\n * defaults stripped: a property carrying one of the\n * {@link USER_PROPERTY_DEFAULTS} normalizes to one omitting it. Only\n * exact default values are removed; `$id` and every other field pass\n * through untouched. Nested `properties` are descended into, so a leaf\n * of an object property normalizes the same way a top-level one does.\n */\nexport function normalizeSchemaBody(body: object): object {\n const result = structuredClone(body) as Record<string, unknown>;\n stripPropertyDefaults(result);\n return result;\n}\n\n/**\n * Strip {@link USER_PROPERTY_DEFAULTS} from every property of an object\n * schema, descending into nested `properties` so a leaf of an object\n * property normalizes the same way a top-level one does.\n */\nfunction stripPropertyDefaults(schema: Record<string, unknown>): void {\n if (!isPlainObject(schema.properties)) {\n return;\n }\n for (const property of Object.values(schema.properties)) {\n if (!isPlainObject(property)) {\n continue;\n }\n for (const [key, defaultValue] of Object.entries(USER_PROPERTY_DEFAULTS)) {\n if (property[key] === defaultValue) {\n delete property[key];\n }\n }\n stripPropertyDefaults(property);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,yBAA4D,EACvE,WAAW,OACZ;;;;;;AAOD,MAAM,qBAAqB;CAAC;CAAM;CAAc;CAAc;CAAc;CAAa;AAEzF,SAAS,cAAc,OAAkD;AACvE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;;;;;;AAQ7E,SAAS,gBAAgB,OAAyB;AAChD,KAAI,CAAC,cAAc,MAAM,CACvB,QAAO;AAET,QAAO,OAAO,QAAQ,MAAM,CAAC,OAC1B,CAAC,KAAK,YACJ,QAAQ,cAAc,QAAQ,eAC9B,UAAU,QAAQ,UAAU,KAAA,KAAc,MAAM,QAAQ,MAAM,IAAI,MAAM,WAAW,GACvF;;;;;;;;;;;AAYH,SAAgB,kBAAkB,MAAsB;CACtD,MAAM,SAAS,gBAAgB,KAAK;AACpC,MAAK,MAAM,OAAO,mBAChB,QAAO,OAAO;AAEhB,QAAO,OAAO;AACd,KAAI,cAAc,UAAU,gBAAgB,OAAO,SAAS,CAC1D,QAAO,OAAO;AAEhB,QAAO;;;;;;;;;;AAWT,SAAgB,oBAAoB,MAAsB;CACxD,MAAM,SAAS,gBAAgB,KAAK;AACpC,uBAAsB,OAAO;AAC7B,QAAO;;;;;;;AAQT,SAAS,sBAAsB,QAAuC;AACpE,KAAI,CAAC,cAAc,OAAO,WAAW,CACnC;AAEF,MAAK,MAAM,YAAY,OAAO,OAAO,OAAO,WAAW,EAAE;AACvD,MAAI,CAAC,cAAc,SAAS,CAC1B;AAEF,OAAK,MAAM,CAAC,KAAK,iBAAiB,OAAO,QAAQ,uBAAuB,CACtE,KAAI,SAAS,SAAS,aACpB,QAAO,SAAS;AAGpB,wBAAsB,SAAS"}
|
package/dist/schemas.d.mts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
1
3
|
//#region src/schemas.d.ts
|
|
2
4
|
declare const schemaConfigSchema: any;
|
|
3
5
|
declare const flowConfigSchema: any;
|
|
4
6
|
declare const createFlowDefinitionRequestSchema: any;
|
|
7
|
+
/** The wire shape of a branding revision (`POST /branding` request body). */
|
|
8
|
+
declare const brandingWireSchema: any;
|
|
9
|
+
/**
|
|
10
|
+
* The local `.zitadel/branding/*.json` dialect: the wire shape plus
|
|
11
|
+
* `liquid_template_file`, a descriptor-relative path the CLI inlines into
|
|
12
|
+
* `liquid_template` when publishing. Exactly one of the two template carriers
|
|
13
|
+
* may be present (the meta-schema `branding.json` mirrors this).
|
|
14
|
+
*/
|
|
15
|
+
declare const brandingConfigSchema: z.ZodObject<{
|
|
16
|
+
[x: string]: any;
|
|
17
|
+
}, z.core.$strict>;
|
|
5
18
|
//#endregion
|
|
6
|
-
export { createFlowDefinitionRequestSchema, flowConfigSchema, schemaConfigSchema };
|
|
19
|
+
export { brandingConfigSchema, brandingWireSchema, createFlowDefinitionRequestSchema, flowConfigSchema, schemaConfigSchema };
|
|
7
20
|
//# sourceMappingURL=schemas.d.mts.map
|
package/dist/schemas.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.mts","names":[],"sources":["../src/schemas.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"schemas.d.mts","names":[],"sources":["../src/schemas.ts"],"mappings":";;;cASa,kBAAA;AAAA,cACA,gBAAA;AAAA,cACA,iCAAA;;cAGA,kBAAA;;;AAJb;;;;cAYa,oBAAA,EAAoB,CAAA,CAAA,SAAA;EAAA;GAuC7B,CAAA,CAAA,IAAA,CAAA,OAAA"}
|
package/dist/schemas.mjs
CHANGED
|
@@ -1,9 +1,69 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isCanonicalLoopbackHttpUrl } from "./branding-url.mjs";
|
|
2
|
+
import { CreateBrandingBody, CreateFlowDefinitionBody, CreateSchemaBody } from "@zitadel/api/generated/endpoints/zitadelNextGen.zod";
|
|
3
|
+
import { z } from "zod";
|
|
2
4
|
//#region src/schemas.ts
|
|
3
5
|
const schemaConfigSchema = CreateSchemaBody;
|
|
4
6
|
const flowConfigSchema = CreateFlowDefinitionBody.shape.flow_definition;
|
|
5
7
|
const createFlowDefinitionRequestSchema = CreateFlowDefinitionBody;
|
|
8
|
+
/** The wire shape of a branding revision (`POST /branding` request body). */
|
|
9
|
+
const brandingWireSchema = CreateBrandingBody;
|
|
10
|
+
/**
|
|
11
|
+
* The local `.zitadel/branding/*.json` dialect: the wire shape plus
|
|
12
|
+
* `liquid_template_file`, a descriptor-relative path the CLI inlines into
|
|
13
|
+
* `liquid_template` when publishing. Exactly one of the two template carriers
|
|
14
|
+
* may be present (the meta-schema `branding.json` mirrors this).
|
|
15
|
+
*/
|
|
16
|
+
const brandingConfigSchema = z.strictObject({
|
|
17
|
+
...CreateBrandingBody.shape,
|
|
18
|
+
logo_url: z.string().optional(),
|
|
19
|
+
hero_url: z.string().optional(),
|
|
20
|
+
$schema: z.string().optional(),
|
|
21
|
+
liquid_template_file: z.string().min(1).optional()
|
|
22
|
+
}).superRefine((value, ctx) => {
|
|
23
|
+
if (value.liquid_template !== void 0 && value.liquid_template_file !== void 0) ctx.addIssue({
|
|
24
|
+
code: "custom",
|
|
25
|
+
message: "Use either liquid_template_file or an inline liquid_template, not both."
|
|
26
|
+
});
|
|
27
|
+
if (value.font_url !== void 0) ctx.addIssue({
|
|
28
|
+
code: "custom",
|
|
29
|
+
message: "font_url is not writable yet (tenant font delivery needs a safe design, see ADR 040); load fonts from the embedding page instead."
|
|
30
|
+
});
|
|
31
|
+
validateBrandingAssetUrl(value.logo_url, "logo_url", ctx);
|
|
32
|
+
validateBrandingAssetUrl(value.hero_url, "hero_url", ctx);
|
|
33
|
+
});
|
|
34
|
+
function validateBrandingAssetUrl(value, field, ctx) {
|
|
35
|
+
if (value === void 0 || value === "") return;
|
|
36
|
+
if (/[\s\\]/.test(value)) {
|
|
37
|
+
ctx.addIssue({
|
|
38
|
+
code: "custom",
|
|
39
|
+
message: `${field} is not a valid URL.`
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (!/^https?:\/\//i.test(value)) {
|
|
44
|
+
ctx.addIssue({
|
|
45
|
+
code: "custom",
|
|
46
|
+
message: `${field} must be an absolute https URL.`
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
let parsed;
|
|
51
|
+
try {
|
|
52
|
+
parsed = new URL(value);
|
|
53
|
+
} catch {
|
|
54
|
+
ctx.addIssue({
|
|
55
|
+
code: "custom",
|
|
56
|
+
message: `${field} is not a valid URL.`
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (parsed.protocol === "http:" && isCanonicalLoopbackHttpUrl(value)) return;
|
|
61
|
+
if (parsed.protocol !== "https:" || parsed.host === "") ctx.addIssue({
|
|
62
|
+
code: "custom",
|
|
63
|
+
message: `${field} must be an absolute https URL (http is allowed for canonical loopback development hosts only).`
|
|
64
|
+
});
|
|
65
|
+
}
|
|
6
66
|
//#endregion
|
|
7
|
-
export { createFlowDefinitionRequestSchema, flowConfigSchema, schemaConfigSchema };
|
|
67
|
+
export { brandingConfigSchema, brandingWireSchema, createFlowDefinitionRequestSchema, flowConfigSchema, schemaConfigSchema };
|
|
8
68
|
|
|
9
69
|
//# sourceMappingURL=schemas.mjs.map
|
package/dist/schemas.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.mjs","names":[],"sources":["../src/schemas.ts"],"sourcesContent":["import {\n CreateFlowDefinitionBody,\n CreateSchemaBody,\n} from \"@zitadel/api/generated/endpoints/zitadelNextGen.zod\";\n\nexport const schemaConfigSchema = CreateSchemaBody;\nexport const flowConfigSchema = CreateFlowDefinitionBody.shape.flow_definition;\nexport const createFlowDefinitionRequestSchema = CreateFlowDefinitionBody;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"schemas.mjs","names":[],"sources":["../src/schemas.ts"],"sourcesContent":["import {\n CreateBrandingBody,\n CreateFlowDefinitionBody,\n CreateSchemaBody,\n} from \"@zitadel/api/generated/endpoints/zitadelNextGen.zod\";\nimport { z } from \"zod\";\n\nimport { isCanonicalLoopbackHttpUrl } from \"./branding-url.js\";\n\nexport const schemaConfigSchema = CreateSchemaBody;\nexport const flowConfigSchema = CreateFlowDefinitionBody.shape.flow_definition;\nexport const createFlowDefinitionRequestSchema = CreateFlowDefinitionBody;\n\n/** The wire shape of a branding revision (`POST /branding` request body). */\nexport const brandingWireSchema = CreateBrandingBody;\n\n/**\n * The local `.zitadel/branding/*.json` dialect: the wire shape plus\n * `liquid_template_file`, a descriptor-relative path the CLI inlines into\n * `liquid_template` when publishing. Exactly one of the two template carriers\n * may be present (the meta-schema `branding.json` mirrors this).\n */\nexport const brandingConfigSchema = z\n .strictObject({\n // Strict, unlike the generated wire schema: a typo like `hero_urll`\n // must fail plan, not silently pass, get ignored by the server, and\n // vanish on canonical write-back. The editor meta-schema already\n // rejects unknown keys; the CLI gate has to agree.\n ...CreateBrandingBody.shape,\n // Raw strings instead of the generated uri fields: those trim and\n // normalize before refinement runs, hiding exactly the forms the wire\n // body still carries verbatim and Go's url.Parse rejects.\n logo_url: z.string().optional(),\n hero_url: z.string().optional(),\n $schema: z.string().optional(),\n liquid_template_file: z.string().min(1).optional(),\n })\n .superRefine((value, ctx) => {\n if (value.liquid_template !== undefined && value.liquid_template_file !== undefined) {\n ctx.addIssue({\n code: \"custom\",\n message: \"Use either liquid_template_file or an inline liquid_template, not both.\",\n });\n }\n // font_url is read-only in v1: the login component loads it as a\n // document-level stylesheet (shadow-scoped @font-face never registers\n // faces), which would give branding.write arbitrary CSS over the embedding\n // page. The server rejects it too; safe delivery is an ADR 040 follow-up.\n if (value.font_url !== undefined) {\n ctx.addIssue({\n code: \"custom\",\n message:\n \"font_url is not writable yet (tenant font delivery needs a safe design, see ADR 040); load fonts from the embedding page instead.\",\n });\n }\n // Mirror the server's asset URL gate (validateBrandingAssetURL in\n // internal/domain/branding_validator.go) so plan rejects what apply would.\n // Apply mutates schemas and flows before branding, so a late 400 here would\n // leave a half-applied run.\n validateBrandingAssetUrl(value.logo_url, \"logo_url\", ctx);\n validateBrandingAssetUrl(value.hero_url, \"hero_url\", ctx);\n });\n\nfunction validateBrandingAssetUrl(\n value: string | undefined,\n field: \"logo_url\" | \"hero_url\",\n ctx: z.RefinementCtx,\n): void {\n if (value === undefined || value === \"\") {\n return;\n }\n // Stricter-or-equal than the Go gate (validateBrandingAssetURL): the WHATWG\n // parser forgives what Go's url.Parse rejects — `https:example.com`,\n // backslashes, and whitespace all get silently normalized. plan must never\n // accept what apply would reject (the reverse is harmless), so reject the\n // raw forms before parsing.\n if (/[\\s\\\\]/.test(value)) {\n ctx.addIssue({ code: \"custom\", message: `${field} is not a valid URL.` });\n return;\n }\n if (!/^https?:\\/\\//i.test(value)) {\n ctx.addIssue({ code: \"custom\", message: `${field} must be an absolute https URL.` });\n return;\n }\n let parsed: URL;\n try {\n parsed = new URL(value);\n } catch {\n ctx.addIssue({ code: \"custom\", message: `${field} is not a valid URL.` });\n return;\n }\n // Loopback HTTP is the dev-posture carve-out (assets served from the app's\n // own dev server). Check the raw URL so WHATWG normalisation cannot make\n // plan accept a host spelling that the Go save gate rejects.\n if (parsed.protocol === \"http:\" && isCanonicalLoopbackHttpUrl(value)) {\n return;\n }\n if (parsed.protocol !== \"https:\" || parsed.host === \"\") {\n ctx.addIssue({\n code: \"custom\",\n message: `${field} must be an absolute https URL (http is allowed for canonical loopback development hosts only).`,\n });\n }\n}\n"],"mappings":";;;;AASA,MAAa,qBAAqB;AAClC,MAAa,mBAAmB,yBAAyB,MAAM;AAC/D,MAAa,oCAAoC;;AAGjD,MAAa,qBAAqB;;;;;;;AAQlC,MAAa,uBAAuB,EACjC,aAAa;CAKZ,GAAG,mBAAmB;CAItB,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;CAC/B,SAAS,EAAE,QAAQ,CAAC,UAAU;CAC9B,sBAAsB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU;CACnD,CAAC,CACD,aAAa,OAAO,QAAQ;AAC3B,KAAI,MAAM,oBAAoB,KAAA,KAAa,MAAM,yBAAyB,KAAA,EACxE,KAAI,SAAS;EACX,MAAM;EACN,SAAS;EACV,CAAC;AAMJ,KAAI,MAAM,aAAa,KAAA,EACrB,KAAI,SAAS;EACX,MAAM;EACN,SACE;EACH,CAAC;AAMJ,0BAAyB,MAAM,UAAU,YAAY,IAAI;AACzD,0BAAyB,MAAM,UAAU,YAAY,IAAI;EACzD;AAEJ,SAAS,yBACP,OACA,OACA,KACM;AACN,KAAI,UAAU,KAAA,KAAa,UAAU,GACnC;AAOF,KAAI,SAAS,KAAK,MAAM,EAAE;AACxB,MAAI,SAAS;GAAE,MAAM;GAAU,SAAS,GAAG,MAAM;GAAuB,CAAC;AACzE;;AAEF,KAAI,CAAC,gBAAgB,KAAK,MAAM,EAAE;AAChC,MAAI,SAAS;GAAE,MAAM;GAAU,SAAS,GAAG,MAAM;GAAkC,CAAC;AACpF;;CAEF,IAAI;AACJ,KAAI;AACF,WAAS,IAAI,IAAI,MAAM;SACjB;AACN,MAAI,SAAS;GAAE,MAAM;GAAU,SAAS,GAAG,MAAM;GAAuB,CAAC;AACzE;;AAKF,KAAI,OAAO,aAAa,WAAW,2BAA2B,MAAM,CAClE;AAEF,KAAI,OAAO,aAAa,YAAY,OAAO,SAAS,GAClD,KAAI,SAAS;EACX,MAAM;EACN,SAAS,GAAG,MAAM;EACnB,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/template.d.ts
|
|
2
|
+
/** The `{% mandatory_gates %}` runtime safety-net tag every template must emit. */
|
|
3
|
+
declare const MANDATORY_GATES_TAG = "mandatory_gates";
|
|
4
|
+
/**
|
|
5
|
+
* Custom Liquid tags the login orchestrator registers beyond the standard
|
|
6
|
+
* set. Validators must register them (as no-ops) or parsing valid templates
|
|
7
|
+
* fails; `@zitadel/components` registers the real implementations.
|
|
8
|
+
*/
|
|
9
|
+
declare const TEMPLATE_TAGS: readonly ["mandatory_gates"];
|
|
10
|
+
/**
|
|
11
|
+
* Byte cap for stored templates. Mirrors `maxLength` on
|
|
12
|
+
* `branding.liquid_template` in the OpenAPI contract and
|
|
13
|
+
* `MaxBrandingTemplateBytes` in the Go gate.
|
|
14
|
+
*/
|
|
15
|
+
declare const MAX_TEMPLATE_BYTES = 131072;
|
|
16
|
+
type TemplateValidationSeverity = "error" | "warning";
|
|
17
|
+
/**
|
|
18
|
+
* Rule catalog with pointers into the Go lexical gate
|
|
19
|
+
* (`internal/domain/branding_validator.go`) where a server-side mirror
|
|
20
|
+
* exists. `parse` and `mandatory-gates` are authoring-only: the server
|
|
21
|
+
* cannot check them (wrong dialect) and render-time behavior degrades
|
|
22
|
+
* safely without them.
|
|
23
|
+
*/
|
|
24
|
+
declare const TEMPLATE_VALIDATION_RULES: {
|
|
25
|
+
readonly parse: {
|
|
26
|
+
readonly goRef: null;
|
|
27
|
+
};
|
|
28
|
+
readonly size: {
|
|
29
|
+
readonly goRef: "MaxBrandingTemplateBytes";
|
|
30
|
+
};
|
|
31
|
+
readonly "no-script-tag": {
|
|
32
|
+
readonly goRef: "brandingScriptTag";
|
|
33
|
+
};
|
|
34
|
+
readonly "no-style-tag": {
|
|
35
|
+
readonly goRef: "brandingStyleTag";
|
|
36
|
+
};
|
|
37
|
+
readonly "no-inline-handlers": {
|
|
38
|
+
readonly goRef: "brandingInlineHandler";
|
|
39
|
+
};
|
|
40
|
+
readonly "no-javascript-urls": {
|
|
41
|
+
readonly goRef: "brandingJavascriptURL";
|
|
42
|
+
};
|
|
43
|
+
readonly "no-raw-filter": {
|
|
44
|
+
readonly goRef: "brandingRawFilter";
|
|
45
|
+
};
|
|
46
|
+
readonly "mandatory-gates": {
|
|
47
|
+
readonly goRef: null;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
type TemplateValidationRuleId = keyof typeof TEMPLATE_VALIDATION_RULES;
|
|
51
|
+
type TemplateValidationIssue = {
|
|
52
|
+
severity: TemplateValidationSeverity;
|
|
53
|
+
rule: TemplateValidationRuleId;
|
|
54
|
+
message: string;
|
|
55
|
+
};
|
|
56
|
+
type BannedPattern = {
|
|
57
|
+
rule: TemplateValidationRuleId;
|
|
58
|
+
pattern: RegExp;
|
|
59
|
+
message: string;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Lexical bans, identical to the Go gate. The inline-handler pattern only
|
|
63
|
+
* matches inside an open tag so Liquid like `{% assign online = true %}`
|
|
64
|
+
* doesn't false-positive; its separator class includes `/` because HTML
|
|
65
|
+
* treats a slash between attributes as whitespace (`<img/onerror=…>`).
|
|
66
|
+
*/
|
|
67
|
+
declare const BANNED_TEMPLATE_PATTERNS: readonly BannedPattern[];
|
|
68
|
+
/**
|
|
69
|
+
* Validates a login template. Pure and non-throwing, like
|
|
70
|
+
* `validateFlowDefinition`: returns an issue list, empty when the template
|
|
71
|
+
* is valid.
|
|
72
|
+
*/
|
|
73
|
+
declare function validateLoginTemplate(template: string): TemplateValidationIssue[];
|
|
74
|
+
//#endregion
|
|
75
|
+
export { BANNED_TEMPLATE_PATTERNS, MANDATORY_GATES_TAG, MAX_TEMPLATE_BYTES, TEMPLATE_TAGS, TEMPLATE_VALIDATION_RULES, TemplateValidationIssue, TemplateValidationRuleId, TemplateValidationSeverity, validateLoginTemplate };
|
|
76
|
+
//# sourceMappingURL=template.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.mts","names":[],"sources":["../src/template.ts"],"mappings":";;cAkBa,mBAAA;;;;;AAOb;cAAa,aAAA;;;;AAOb;;cAAa,kBAAA;AAAA,KAED,0BAAA;;AAAZ;;;;;AASA;cAAa,yBAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;KAWD,wBAAA,gBAAwC,yBAAA;AAAA,KAExC,uBAAA;EACV,QAAA,EAAU,0BAAA;EACV,IAAA,EAAM,wBAAA;EACN,OAAA;AAAA;AAAA,KAGG,aAAA;EACH,IAAA,EAAM,wBAAA;EACN,OAAA,EAAS,MAAA;EACT,OAAA;AAAA;AALA;;;;;;AAAA,cAcW,wBAAA,WAAmC,aAAA;;;;;AAAhD;iBAqEgB,qBAAA,CAAsB,QAAA,WAAmB,uBAAA"}
|