@zitadel/config 1.0.0-alpha.23 → 1.0.0-alpha.25

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.
Files changed (62) hide show
  1. package/defaults/README-branding.md +26 -34
  2. package/defaults/branding/centered/login.liquid +31 -11
  3. package/defaults/branding/minimal/login.liquid +30 -10
  4. package/dist/branding-contrast.d.mts +28 -21
  5. package/dist/branding-contrast.d.mts.map +1 -1
  6. package/dist/branding-contrast.mjs +1 -1
  7. package/dist/branding-contrast.mjs.map +1 -1
  8. package/dist/branding-url.d.mts +1 -2
  9. package/dist/branding-url.d.mts.map +1 -1
  10. package/dist/branding-url.mjs.map +1 -1
  11. package/dist/css-color.d.mts +5 -10
  12. package/dist/css-color.d.mts.map +1 -1
  13. package/dist/css-color.mjs +1 -1
  14. package/dist/css-color.mjs.map +1 -1
  15. package/dist/{defaults-BxGCGdqS.d.mts → defaults-BtKIcWxW.d.mts} +21 -14
  16. package/dist/defaults-BtKIcWxW.d.mts.map +1 -0
  17. package/dist/{defaults-DV6hY2KT.mjs → defaults-COLBFU3S.mjs} +17 -18
  18. package/dist/defaults-COLBFU3S.mjs.map +1 -0
  19. package/dist/defaults.d.mts +1 -1
  20. package/dist/defaults.mjs +1 -1
  21. package/dist/idp/index.d.mts +2 -0
  22. package/dist/idp/index.mjs +2 -0
  23. package/dist/idp-ByzXQN85.mjs +207 -0
  24. package/dist/idp-ByzXQN85.mjs.map +1 -0
  25. package/dist/idp-connection-CHxGm1Wr.mjs +324 -0
  26. package/dist/idp-connection-CHxGm1Wr.mjs.map +1 -0
  27. package/dist/index-Cuxu-zQD.d.mts +210 -0
  28. package/dist/index-Cuxu-zQD.d.mts.map +1 -0
  29. package/dist/index.d.mts +4 -3
  30. package/dist/index.mjs +5 -4
  31. package/dist/{meta-schemas-OowgSUfU.mjs → meta-schemas-C03zgFux.mjs} +36 -333
  32. package/dist/meta-schemas-C03zgFux.mjs.map +1 -0
  33. package/dist/meta-schemas.d.mts +5 -6
  34. package/dist/meta-schemas.d.mts.map +1 -1
  35. package/dist/meta-schemas.mjs +1 -1
  36. package/dist/normalize.d.mts +3 -4
  37. package/dist/normalize.d.mts.map +1 -1
  38. package/dist/normalize.mjs.map +1 -1
  39. package/dist/{schemas-7oNU25Qz.mjs → schemas-CUU4Rf8J.mjs} +11 -4
  40. package/dist/schemas-CUU4Rf8J.mjs.map +1 -0
  41. package/dist/schemas.d.mts +361 -9
  42. package/dist/schemas.d.mts.map +1 -1
  43. package/dist/schemas.mjs +2 -2
  44. package/dist/template.d.mts +9 -10
  45. package/dist/template.d.mts.map +1 -1
  46. package/dist/template.mjs +1 -1
  47. package/dist/template.mjs.map +1 -1
  48. package/dist/validate.d.mts +15 -13
  49. package/dist/validate.d.mts.map +1 -1
  50. package/dist/validate.mjs +28 -18
  51. package/dist/validate.mjs.map +1 -1
  52. package/meta-schemas/branding.json +5 -5
  53. package/meta-schemas/flow-definition.json +81 -6
  54. package/meta-schemas/idp-connection.json +6 -6
  55. package/package.json +18 -8
  56. package/defaults/branding/hero/login.liquid +0 -238
  57. package/defaults/branding/split/login.liquid +0 -197
  58. package/defaults/branding/split-right/login.liquid +0 -197
  59. package/dist/defaults-BxGCGdqS.d.mts.map +0 -1
  60. package/dist/defaults-DV6hY2KT.mjs.map +0 -1
  61. package/dist/meta-schemas-OowgSUfU.mjs.map +0 -1
  62. package/dist/schemas-7oNU25Qz.mjs.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { t as idp_connection_default } from "./idp-connection-CHxGm1Wr.mjs";
1
2
  //#region meta-schemas/auth-method.json
2
3
  var auth_method_default = {
3
4
  $comment: "Code generated by scripts/generate-meta-schemas.ts; DO NOT EDIT.",
@@ -46,7 +47,7 @@ var branding_default = {
46
47
  "layout": {
47
48
  "type": "string",
48
49
  "enum": ["centered", "split"],
49
- "description": "Degrade preset (`centered` or `split`) the bundled default template\nbranches on, and the fallback when a custom template fails validation.\nNot the complete design catalog: all named designs (`centered`, `split`,\n`split-right`, `hero`, `minimal`) ship as templates and map onto one of\nthese two values; switch designs with\n`zitadel branding eject --design <name>` instead of editing this field.\nEjected templates may ignore it. Selects a template rather than restyling\nthe widget, so it is not an appearance control.\n",
50
+ "description": "Layout preset the revision declares, `centered` or `split`. It is\nvalidated on publish and handed to the template as `branding.layout`.\nThe bundled default template does not read it, so changing it alone\nchanges nothing on screen, and it is not an appearance control.\nNot a design catalog: the ejectable designs (`centered`, `minimal`)\nship as templates and both carry `centered`; switch designs with\n`zitadel branding eject --design <name>` instead of editing this field.\n`split` is carried by revisions published from the earlier split\ndesigns.\n",
50
51
  "default": "centered"
51
52
  },
52
53
  "liquid_template": {
@@ -68,7 +69,7 @@ var branding_default = {
68
69
  "type": "string",
69
70
  "format": "uri",
70
71
  "maxLength": 2048,
71
- "description": "Hero/background image URL, used by the split layout.",
72
+ "description": "Hero image URL, for templates that reference `branding.hero_url`. The\nbundled default template does not use it.\n",
72
73
  "pattern": "^([hH][tT][tT][pP][sS]://(?:\\[[0-9A-Fa-f:.]+\\]|[^/:?#@]+)(?::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]))?(?:[/?#]|$)|[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]))?([/?#]|$))"
73
74
  },
74
75
  "theme": { "$ref": "#/$defs/BrandingTheme" },
@@ -89,7 +90,7 @@ var branding_default = {
89
90
  "BrandingTheme": {
90
91
  "type": "object",
91
92
  "additionalProperties": false,
92
- "description": "The theme sides this project publishes, and which of them may run.\n\nPublishing one side makes it the only theme that resolves, even under `auto`\nor an operating-system preference for the other. Publishing both lets `auto`\nfollow the operating system, each side using its own logo and palette.\n\nThe element-level `theme` property overrides `mode`, but cannot select a\nside the project never published.\n",
93
+ "description": "The theme sides this project publishes, and which of them may run.\n\nPublishing one side makes it the only theme that resolves, even under `auto`\nor an operating-system preference for the other. Publishing both lets `auto`\nfollow the operating system, each side using its own logo and palette.\n\nThe element-level `theme` property overrides `mode`. When one side is\npublished it cannot select the other. Publishing neither side leaves\n`mode`, the `theme` property and the operating system to pick one, painted\nwith the maintained defaults.\n",
93
94
  "properties": {
94
95
  "mode": {
95
96
  "type": "string",
@@ -108,7 +109,7 @@ var branding_default = {
108
109
  "BrandingThemeSide": {
109
110
  "type": "object",
110
111
  "additionalProperties": false,
111
- "description": "One complete theme side. Light and dark are independent surfaces — neither\ninherits from the other, and a side that is absent is never used, whatever\n`mode` or the operating system asks for.\n",
112
+ "description": "One complete theme side. Light and dark are independent surfaces — neither\ninherits from the other. When a revision publishes one side, the absent\nside is never used, whatever `mode` or the operating system asks for. When\nit publishes neither, either side can resolve and paints the maintained\ndefaults.\n",
112
113
  "properties": {
113
114
  "logo_url": {
114
115
  "type": "string",
@@ -213,7 +214,7 @@ var branding_default = {
213
214
  "type": "string",
214
215
  "format": "uri",
215
216
  "maxLength": 2048,
216
- "description": "Stylesheet that loads `font_family` — a Google Fonts CSS URL, for\nexample. Must be https; unlike the logo and hero assets there is no\nloopback carve-out. Rejected without `font_family`, which would name\nnothing to paint with.\n\nStored but not applied to an embedded widget: the embedding page loads\nthe face, and Zitadel does not inject a stylesheet into a document it\ndoes not own.\n",
217
+ "description": "Stylesheet that loads `font_family` — a Google Fonts CSS URL, for\nexample. Must be https; unlike the logo and hero assets there is no\nloopback carve-out. Rejected without `font_family`, which would name\nnothing to paint with.\n\nLoaded only when the login component runs as `variant=\"page\"`, the\nfull-page login. In the default `variant=\"widget\"` it is stored but not\nloaded: the component injects no stylesheet, and the embedding page\nloads the face.\n",
217
218
  "pattern": "^[hH][tT][tT][pP][sS]://(?:\\[[0-9A-Fa-f:.]+\\]|[^/:?#@]+)(?::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]))?(?:[/?#]|$)",
218
219
  "examples": ["https://fonts.googleapis.com/css2?family=Inter&display=swap"]
219
220
  },
@@ -402,7 +403,7 @@ var flow_definition_default = {
402
403
  {
403
404
  "required": ["sso_providers"],
404
405
  "properties": { "sso_providers": { "minItems": 1 } },
405
- "anyOf": [{ "not": { "required": ["transitions"] } }, { "properties": { "transitions": { "not": { "required": ["callback"] } } } }]
406
+ "anyOf": [{ "not": { "required": ["transitions"] } }, { "properties": { "transitions": { "not": { "required": ["sso_authenticated"] } } } }]
406
407
  },
407
408
  {
408
409
  "required": ["complete"],
@@ -436,8 +437,30 @@ var flow_definition_default = {
436
437
  "actions": { "maxItems": 0 },
437
438
  "sso_providers": { "maxItems": 0 },
438
439
  "gates": { "maxProperties": 0 },
439
- "transitions": { "not": { "required": ["callback"] } }
440
+ "transitions": { "not": { "required": ["sso_authenticated"] } }
440
441
  }
442
+ },
443
+ {
444
+ "required": ["transitions"],
445
+ "properties": { "transitions": { "anyOf": [{
446
+ "required": ["sso_authenticated"],
447
+ "properties": { "sso_authenticated": { "anyOf": [{
448
+ "required": ["purpose"],
449
+ "properties": { "purpose": { "type": "string" } }
450
+ }, {
451
+ "required": ["action"],
452
+ "properties": { "action": { "type": "string" } }
453
+ }] } }
454
+ }, {
455
+ "required": ["sso_user_not_found"],
456
+ "properties": { "sso_user_not_found": { "anyOf": [{
457
+ "required": ["purpose"],
458
+ "properties": { "purpose": { "type": "string" } }
459
+ }, {
460
+ "required": ["action"],
461
+ "properties": { "action": { "type": "string" } }
462
+ }] } }
463
+ }] } }
441
464
  }
442
465
  ] },
443
466
  "description": "A step in a flow definition. This is the admin-authored configuration,\nnot the runtime payload sent to the frontend.\n\nSteps are schema-driven: `fields` references properties from the flow's\nuser schema, and the engine resolves field metadata (type, validation,\nimplicit outcomes) from schema annotations at runtime.\n\nA step with only `complete` set is a terminal step. All other steps are\nrendered to the frontend as capability payloads.\n",
@@ -482,8 +505,9 @@ var flow_definition_default = {
482
505
  },
483
506
  "sso_providers": {
484
507
  "type": "array",
485
- "description": "Slugs of the identity provider connections this step offers, in display\norder. Each names the `slug` of a connection under `.zitadel/idps/`; the\nconnection carries the display name and template, so a rename there\nreaches every step without editing the flow. The rendered step the client\nreceives carries the resolved `{id, name, template}` objects instead.\n",
508
+ "description": "Slugs of the identity provider connections this step offers, in display\norder. Each names the `slug` of a connection under `.zitadel/idps/`; the\nconnection carries the display name and template, so a rename there\nreaches every step without editing the flow. The rendered step the client\nreceives carries the resolved `{id, name, template}` objects instead. A\nstep offers at most 20 providers.\n",
486
509
  "uniqueItems": true,
510
+ "maxItems": 20,
487
511
  "items": {
488
512
  "type": "string",
489
513
  "pattern": "^[a-z0-9][a-z0-9_-]*$",
@@ -494,8 +518,8 @@ var flow_definition_default = {
494
518
  },
495
519
  "on_success": {
496
520
  "type": "string",
497
- "enum": ["create_user"],
498
- "description": "Server-side mutation to execute when this step completes successfully.\nRuns after field validation passes, before the transition fires.\n\n- create_user: creates the user record (registration flows)\n",
521
+ "enum": ["create_user", "create_user_with_sso"],
522
+ "description": "Server-side mutation to execute when this step completes successfully.\nRuns after field validation passes, before the transition fires.\n\n- create_user: creates the user record (registration flows)\n- create_user_with_sso: creates the user record from the identity an\n external provider returned, linking it to that identity. Accepted by\n the API so an SSO flow can be authored and stored; the engine handler\n is not wired yet, and a step reaching it fails with a flow integrity\n error rather than creating anything.\n",
499
523
  "examples": ["create_user"]
500
524
  },
501
525
  "complete": {
@@ -506,7 +530,7 @@ var flow_definition_default = {
506
530
  },
507
531
  "transitions": {
508
532
  "type": "object",
509
- "description": "Maps action/outcome names to their transition descriptor.\n\nKeys match action names from the `actions` array. Additional keys\ncome from implicit outcomes based on schema annotations\n(e.g. `user_not_found` from `x-unique` fields) and engine\nevents (e.g. `sso`, `callback`).\n",
533
+ "description": "Maps action/outcome names to their transition descriptor.\n\nKeys match action names from the `actions` array. Additional keys\ncome from implicit outcomes based on schema annotations\n(e.g. `user_not_found` from `x-unique` fields) and engine\nevents (e.g. `sso_authenticated`, `sso_user_not_found`).\n",
510
534
  "additionalProperties": {
511
535
  "type": "object",
512
536
  "required": ["target"],
@@ -625,327 +649,6 @@ var flow_definition_default = {
625
649
  }
626
650
  };
627
651
  //#endregion
628
- //#region meta-schemas/idp-connection.json
629
- var idp_connection_default = {
630
- $comment: "Code generated by scripts/generate-meta-schemas.ts; DO NOT EDIT.",
631
- $schema: "https://json-schema.org/draft/2020-12/schema",
632
- title: "IdentityProviderConnection",
633
- type: "object",
634
- required: [
635
- "slug",
636
- "protocol",
637
- "display_name"
638
- ],
639
- additionalProperties: false,
640
- properties: {
641
- "$schema": {
642
- "type": "string",
643
- "minLength": 1,
644
- "description": "Editor affordance: path or URL of this schema so editors validate and autocomplete the file (scaffolded connection files point at `.zitadel/meta/idp-connection.json`). Ignored by the platform."
645
- },
646
- "slug": {
647
- "type": "string",
648
- "pattern": "^[a-z0-9][a-z0-9_-]*$",
649
- "maxLength": 64,
650
- "description": "Stable identifier, referenced by user schemas (x-auth-methods.sso.providers) and flow steps (sso_providers). Unique within the Project.",
651
- "examples": [
652
- "google",
653
- "github",
654
- "corp_idp"
655
- ]
656
- },
657
- "protocol": {
658
- "type": "string",
659
- "enum": ["oidc", "oauth2"],
660
- "description": "The protocol used by the identity provider."
661
- },
662
- "template": {
663
- "type": "string",
664
- "examples": [
665
- "google",
666
- "github",
667
- "okta"
668
- ],
669
- "description": "Template hint for rendering the provider (logo, colors). Carries no protocol behavior: conduct comes only from the protocol blocks."
670
- },
671
- "display_name": {
672
- "type": "string",
673
- "minLength": 1,
674
- "maxLength": 256,
675
- "description": "Shown to the user on the sign-in button."
676
- },
677
- "subject_claim": {
678
- "type": "string",
679
- "minLength": 1,
680
- "description": "Claim carrying the provider's stable subject identifier. Optional for OIDC (the engine uses `sub` per spec; override where `sub` is pairwise, e.g. Entra `oid`). Required for OAuth2 — no `sub` guarantee."
681
- },
682
- "claim_mapping": {
683
- "type": "object",
684
- "additionalProperties": {
685
- "type": "string",
686
- "pattern": "^[^$]"
687
- },
688
- "description": "Maps user-schema property names (keys) to provider claim names (values). A value is an exact top-level claim key: no path syntax, and a dot is part of the key, so nested claims (OIDC `address`, GitHub `plan`) are not addressable yet. `$`-prefixed strings and non-string values are reserved for future mapping forms. Add properties defined in the user schemas; unmatched entries are ignored."
689
- },
690
- "verified_claims": {
691
- "type": "object",
692
- "additionalProperties": { "anyOf": [
693
- { "const": true },
694
- { "const": "$supplementary_fetch" },
695
- {
696
- "type": "string",
697
- "pattern": "^[^$]"
698
- }
699
- ] },
700
- "description": "Maps a verifiable user-schema property to its verification source: a claim name (read the claim), the literal `true` (trust this provider unconditionally), or the `$`-pointer `\"$supplementary_fetch\"` (verified by the selected supplementary_fetch strategy per its contract - e.g. github_primary_email verifies `email` via primary && verified). Values starting with `$` are reserved; only `$supplementary_fetch` is defined. Distinct from claim_mapping, which carries values, not verification state.",
701
- "examples": [
702
- { "email": "email_verified" },
703
- { "email": true },
704
- { "email": "$supplementary_fetch" }
705
- ]
706
- },
707
- "provisioning": {
708
- "type": "object",
709
- "additionalProperties": false,
710
- "description": "How far this provider's data is trusted. `creation` defines what happens to a subject that has no account yet. With `disabled`, a user is not created. With `auto`, the user is created without a collection step when the mapped claims fill every required property of the schema. A required property with an `x-unique` scope must also arrive verified according to `verified_claims`. When a required property is missing or such a property is unverified, the collection step is shown instead, prefilled with the claims. Gating on other properties will be added when `x-verify` returns to the dialect. The following are deferred and will be added as additive changes. The `auto_only` value creates the user from complete claims or fails without collecting. The linking policy (`is_linking_allowed`, `auto_linking`), together with the account-linking journey. `is_auto_update`, together with per-property verification state.",
711
- "properties": { "creation": {
712
- "type": "string",
713
- "enum": ["disabled", "auto"],
714
- "default": "auto"
715
- } }
716
- },
717
- "oidc": {
718
- "type": "object",
719
- "additionalProperties": false,
720
- "description": "OIDC connection details. Endpoints come from discovery, but any may be supplied to override it, or to serve a provider that exposes no .well-known document.",
721
- "required": [
722
- "issuer",
723
- "client_id",
724
- "client_secret",
725
- "scopes"
726
- ],
727
- "properties": {
728
- "issuer": {
729
- "type": "string",
730
- "format": "uri",
731
- "pattern": "^(https://[^\\s/?#]+|http://(localhost|127\\.0\\.0\\.1)(:[0-9]+)?)(/[^\\s?#]*)?$",
732
- "description": "Discovery base. Endpoints are resolved from its .well-known document unless overridden below."
733
- },
734
- "jwks_uri": {
735
- "type": "string",
736
- "format": "uri",
737
- "pattern": "^(https://[^\\s/?#]+|http://(localhost|127\\.0\\.0\\.1)(:[0-9]+)?)([/?][^\\s#]*)?$",
738
- "description": "Overrides the JWKS endpoint from discovery. Required in practice when the provider exposes no .well-known document, since ID tokens cannot otherwise be validated."
739
- },
740
- "id_token_mapping": {
741
- "type": "boolean",
742
- "default": false,
743
- "description": "Read user claims from the id_token instead of the userinfo endpoint, for providers that populate only the former."
744
- },
745
- "authorization_endpoint": {
746
- "type": "string",
747
- "format": "uri",
748
- "pattern": "^(https://[^\\s/?#]+|http://(localhost|127\\.0\\.0\\.1)(:[0-9]+)?)([/?][^\\s#]*)?$",
749
- "description": "Overrides discovery."
750
- },
751
- "token_endpoint": {
752
- "type": "string",
753
- "format": "uri",
754
- "pattern": "^(https://[^\\s/?#]+|http://(localhost|127\\.0\\.0\\.1)(:[0-9]+)?)([/?][^\\s#]*)?$",
755
- "description": "Overrides discovery."
756
- },
757
- "userinfo_endpoint": {
758
- "type": "string",
759
- "format": "uri",
760
- "pattern": "^(https://[^\\s/?#]+|http://(localhost|127\\.0\\.0\\.1)(:[0-9]+)?)([/?][^\\s#]*)?$",
761
- "description": "Overrides discovery."
762
- },
763
- "client_id": {
764
- "type": "string",
765
- "minLength": 1,
766
- "maxLength": 512,
767
- "description": "Either a literal in case the same client is used in all environments, or a `${{ NAME }}` reference to a variable in case of separate clients per environment."
768
- },
769
- "client_secret": {
770
- "type": "string",
771
- "pattern": "^\\$\\{\\{ *[A-Za-z0-9_]+ *\\}\\}$",
772
- "description": "A reference to the variable holding the client secret, never the secret itself. A secret may only be referenced as the entire value: text around the placeholder would be rendered into the resolved credential, so it is rejected. The engine resolves it against the environment serving the request."
773
- },
774
- "token_endpoint_auth_method": {
775
- "type": "string",
776
- "enum": ["client_secret_basic", "client_secret_post"],
777
- "default": "client_secret_basic",
778
- "description": "How the client authenticates at the token endpoint."
779
- },
780
- "scopes": {
781
- "type": "array",
782
- "items": {
783
- "type": "string",
784
- "minLength": 1
785
- },
786
- "uniqueItems": true,
787
- "minItems": 1,
788
- "contains": { "const": "openid" },
789
- "default": [
790
- "openid",
791
- "profile",
792
- "email"
793
- ],
794
- "description": "Must contain `openid` - without it the provider returns no id_token and the connection is not OIDC. Enforced by `required` + `contains`; the `default` is a UI-prefill hint only - validators never inject it, so an omitted `scopes` is rejected regardless."
795
- },
796
- "pkce_enabled": {
797
- "type": "boolean",
798
- "default": true,
799
- "description": "Send PKCE (code_challenge/code_verifier). Providers that do not support PKCE typically ignore the parameters, so `true` is safe and future-proofs; set `false` only for a provider whose token endpoint rejects them."
800
- },
801
- "static_authorize_parameters": {
802
- "type": "object",
803
- "propertyNames": {
804
- "minLength": 1,
805
- "not": { "enum": [
806
- "client_id",
807
- "redirect_uri",
808
- "response_type",
809
- "scope",
810
- "state",
811
- "nonce",
812
- "code_challenge",
813
- "code_challenge_method",
814
- "response_mode",
815
- "request",
816
- "request_uri",
817
- "client_secret",
818
- "client_assertion"
819
- ] }
820
- },
821
- "additionalProperties": { "type": "string" },
822
- "description": "Extra provider-specific authorize parameters (for example `prompt`). Engine-owned protocol parameters are reserved and rejected by `propertyNames`: the engine composes them itself, and an override of `state` or `nonce` would silently defeat CSRF and token binding. `client_secret` and `client_assertion` are rejected as they could be appended to the public authorize URL."
823
- }
824
- }
825
- },
826
- "oauth2": {
827
- "type": "object",
828
- "additionalProperties": false,
829
- "description": "OAuth 2.0 connection details.",
830
- "required": [
831
- "authorization_endpoint",
832
- "token_endpoint",
833
- "userinfo_endpoint",
834
- "client_id",
835
- "client_secret"
836
- ],
837
- "properties": {
838
- "authorization_endpoint": {
839
- "type": "string",
840
- "format": "uri",
841
- "pattern": "^(https://[^\\s/?#]+|http://(localhost|127\\.0\\.0\\.1)(:[0-9]+)?)([/?][^\\s#]*)?$",
842
- "description": "Where the user is sent to authorize."
843
- },
844
- "token_endpoint": {
845
- "type": "string",
846
- "format": "uri",
847
- "pattern": "^(https://[^\\s/?#]+|http://(localhost|127\\.0\\.0\\.1)(:[0-9]+)?)([/?][^\\s#]*)?$",
848
- "description": "Where the authorization code is exchanged."
849
- },
850
- "userinfo_endpoint": {
851
- "type": "string",
852
- "format": "uri",
853
- "pattern": "^(https://[^\\s/?#]+|http://(localhost|127\\.0\\.0\\.1)(:[0-9]+)?)([/?][^\\s#]*)?$",
854
- "description": "Where the authenticated user's profile is fetched."
855
- },
856
- "client_id": {
857
- "type": "string",
858
- "minLength": 1,
859
- "maxLength": 512,
860
- "description": "Either a literal in case the same client is used in all environments, or a `${{ NAME }}` reference to a variable in case of separate clients per environment."
861
- },
862
- "client_secret": {
863
- "type": "string",
864
- "pattern": "^\\$\\{\\{ *[A-Za-z0-9_]+ *\\}\\}$",
865
- "description": "A reference to the variable holding the client secret, never the secret itself. A secret may only be referenced as the entire value: text around the placeholder would be rendered into the resolved credential, so it is rejected. The engine resolves it against the environment serving the request."
866
- },
867
- "token_endpoint_auth_method": {
868
- "type": "string",
869
- "enum": ["client_secret_basic", "client_secret_post"],
870
- "default": "client_secret_basic",
871
- "description": "How the client authenticates at the token endpoint."
872
- },
873
- "scopes": {
874
- "type": "array",
875
- "items": {
876
- "type": "string",
877
- "minLength": 1
878
- },
879
- "uniqueItems": true,
880
- "description": "Provider-specific; OAuth 2.0 defines no universal scope."
881
- },
882
- "pkce_enabled": {
883
- "type": "boolean",
884
- "default": true,
885
- "description": "Send PKCE (code_challenge/code_verifier). Providers that do not support PKCE typically ignore the parameters, so `true` is safe and future-proofs; set `false` only for a provider whose token endpoint rejects them."
886
- },
887
- "static_authorize_parameters": {
888
- "type": "object",
889
- "propertyNames": {
890
- "minLength": 1,
891
- "not": { "enum": [
892
- "client_id",
893
- "redirect_uri",
894
- "response_type",
895
- "scope",
896
- "state",
897
- "nonce",
898
- "code_challenge",
899
- "code_challenge_method",
900
- "response_mode",
901
- "request",
902
- "request_uri",
903
- "client_secret",
904
- "client_assertion"
905
- ] }
906
- },
907
- "additionalProperties": { "type": "string" },
908
- "description": "Extra provider-specific authorize parameters (for example `prompt`). Engine-owned protocol parameters are reserved and rejected by `propertyNames`: the engine composes them itself, and an override of `state` or `nonce` would silently defeat CSRF and token binding. `client_secret` and `client_assertion` are rejected as they could be appended to the public authorize URL."
909
- },
910
- "supplementary_fetch": {
911
- "type": "string",
912
- "enum": ["github_primary_email"],
913
- "description": "Provider-specific follow-up call after userinfo. New values are non-breaking."
914
- }
915
- },
916
- "allOf": [{
917
- "if": {
918
- "properties": { "supplementary_fetch": { "const": "github_primary_email" } },
919
- "required": ["supplementary_fetch"]
920
- },
921
- "then": {
922
- "required": ["scopes"],
923
- "properties": { "scopes": { "contains": { "const": "user:email" } } }
924
- }
925
- }]
926
- }
927
- },
928
- allOf: [{
929
- "if": {
930
- "properties": { "protocol": { "const": "oidc" } },
931
- "required": ["protocol"]
932
- },
933
- "then": {
934
- "required": ["oidc"],
935
- "properties": { "oauth2": false }
936
- }
937
- }, {
938
- "if": {
939
- "properties": { "protocol": { "const": "oauth2" } },
940
- "required": ["protocol"]
941
- },
942
- "then": {
943
- "required": ["oauth2", "subject_claim"],
944
- "properties": { "oidc": false }
945
- }
946
- }]
947
- };
948
- //#endregion
949
652
  //#region meta-schemas/property-name.json
950
653
  var property_name_default = {
951
654
  $comment: "Code generated by scripts/generate-meta-schemas.ts; DO NOT EDIT.",
@@ -1170,4 +873,4 @@ function metaSchemaFiles() {
1170
873
  //#endregion
1171
874
  export { metaSchemaFiles as i, FLOW_FILE_SCHEMA_REF as n, META_SCHEMA_DIR as r, BRANDING_FILE_SCHEMA_REF as t };
1172
875
 
1173
- //# sourceMappingURL=meta-schemas-OowgSUfU.mjs.map
876
+ //# sourceMappingURL=meta-schemas-C03zgFux.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta-schemas-C03zgFux.mjs","names":["flowDefinitionMetaSchema","userSchemaMetaSchema","userPropertyMetaSchema","propertyNameMetaSchema","authMethodsMetaSchema","authMethodMetaSchema","ssoAuthMethodMetaSchema","idpConnectionMetaSchema","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/sso-auth-method.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 generated: `scripts/sync-meta-schemas.mjs`\n * copies them from `api/openapi/endpoints/schemas/*.json` (the files the\n * server embeds) ahead of build, typecheck and test. Those are generated in\n * turn from the OpenAPI YAML by `scripts/generate-meta-schemas.ts` at the repo\n * root, so the YAML is the only thing to edit.\n */\nimport authMethodMetaSchema from \"../meta-schemas/auth-method.json\" with { type: \"json\" };\nimport authMethodsMetaSchema from \"../meta-schemas/auth-methods.json\" with { type: \"json\" };\nimport brandingMetaSchema from \"../meta-schemas/branding.json\" with { type: \"json\" };\nimport flowDefinitionMetaSchema from \"../meta-schemas/flow-definition.json\" with { type: \"json\" };\nimport idpConnectionMetaSchema from \"../meta-schemas/idp-connection.json\" with { type: \"json\" };\nimport propertyNameMetaSchema from \"../meta-schemas/property-name.json\" with { type: \"json\" };\nimport ssoAuthMethodMetaSchema from \"../meta-schemas/sso-auth-method.json\" with { type: \"json\" };\nimport userPropertyMetaSchema from \"../meta-schemas/user-property.json\" with { type: \"json\" };\nimport userSchemaMetaSchema from \"../meta-schemas/user-schema.json\" with { type: \"json\" };\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`,\n * `auth-method.json`, and `sso-auth-method.json` are pulled in by\n * `user-schema.json`'s relative `$ref`s — without them the copied dialect\n * 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: \"sso-auth-method.json\", body: ssoAuthMethodMetaSchema as object },\n { name: \"idp-connection.json\", body: idpConnectionMetaSchema as object },\n { name: \"branding.json\", body: brandingMetaSchema as object },\n ];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AQwBA,MAAa,kBAAkB;;;;;AAM/B,MAAa,uBAAuB;;;;;AAMpC,MAAa,2BAA2B;;;;;;;AAUxC,SAAgB,kBAAiD;CAC/D,OAAO;EACL;GAAE,MAAM;GAAwB,MAAMA;EAAmC;EACzE;GAAE,MAAM;GAAoB,MAAMC;EAA+B;EACjE;GAAE,MAAM;GAAsB,MAAMC;EAAiC;EACrE;GAAE,MAAM;GAAsB,MAAMC;EAAiC;EACrE;GAAE,MAAM;GAAqB,MAAMC;EAAgC;EACnE;GAAE,MAAM;GAAoB,MAAMC;EAA+B;EACjE;GAAE,MAAM;GAAwB,MAAMC;EAAkC;EACxE;GAAE,MAAM;GAAuB,MAAMC;EAAkC;EACvE;GAAE,MAAM;GAAiB,MAAMC;EAA6B;CAC9D;AACF"}
@@ -1,17 +1,17 @@
1
1
  //#region src/meta-schemas.d.ts
2
2
  /** Project-relative directory setup writes the meta-schemas to. */
3
- declare const META_SCHEMA_DIR = ".zitadel/meta";
3
+ export declare const META_SCHEMA_DIR = ".zitadel/meta";
4
4
  /**
5
5
  * The `$schema` value scaffolded flow files carry, relative to
6
6
  * `.zitadel/flows/` — resolves to `{@link META_SCHEMA_DIR}/flow-definition.json`.
7
7
  */
8
- declare const FLOW_FILE_SCHEMA_REF = "../meta/flow-definition.json";
8
+ export declare const FLOW_FILE_SCHEMA_REF = "../meta/flow-definition.json";
9
9
  /**
10
10
  * The `$schema` value scaffolded branding files carry, relative to
11
11
  * `.zitadel/branding/` — resolves to `{@link META_SCHEMA_DIR}/branding.json`.
12
12
  */
13
- declare const BRANDING_FILE_SCHEMA_REF = "../meta/branding.json";
14
- type MetaSchemaFile = {
13
+ export declare const BRANDING_FILE_SCHEMA_REF = "../meta/branding.json";
14
+ export type MetaSchemaFile = {
15
15
  name: string;
16
16
  body: object;
17
17
  };
@@ -21,7 +21,6 @@ type MetaSchemaFile = {
21
21
  * `user-schema.json`'s relative `$ref`s — without them the copied dialect
22
22
  * cannot resolve offline.
23
23
  */
24
- declare function metaSchemaFiles(): ReadonlyArray<MetaSchemaFile>;
24
+ export declare function metaSchemaFiles(): ReadonlyArray<MetaSchemaFile>;
25
25
  //#endregion
26
- export { BRANDING_FILE_SCHEMA_REF, FLOW_FILE_SCHEMA_REF, META_SCHEMA_DIR, MetaSchemaFile, metaSchemaFiles };
27
26
  //# sourceMappingURL=meta-schemas.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"meta-schemas.d.mts","names":[],"sources":["../src/meta-schemas.ts"],"mappings":";;cA0Ca,eAAA;;;;;cAMA,oBAAA;;;;;cAMA,wBAAA;AAAA,KAED,cAAA;EAAmB,IAAA;EAAc,IAAA;AAAA;AAA7C;;;;;AAQA;AARA,iBAQgB,eAAA,CAAA,GAAmB,aAAA,CAAc,cAAA"}
1
+ {"version":3,"file":"meta-schemas.d.mts","names":[],"sources":["../src/meta-schemas.ts"],"mappings":";;qBAwBa;;;;;qBAMA;;;;;qBAMA;YAED;EAAmB;EAAc;;;;;;;;wBAQ7B,mBAAmB,cAAc"}
@@ -1,2 +1,2 @@
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-OowgSUfU.mjs";
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-C03zgFux.mjs";
2
2
  export { BRANDING_FILE_SCHEMA_REF, FLOW_FILE_SCHEMA_REF, META_SCHEMA_DIR, metaSchemaFiles };
@@ -22,7 +22,7 @@
22
22
  * spells one of these out is semantically identical to one that omits it:
23
23
  * audit payloads deny by default.
24
24
  */
25
- declare const USER_PROPERTY_DEFAULTS: Readonly<Record<string, boolean>>;
25
+ export declare const USER_PROPERTY_DEFAULTS: Readonly<Record<string, boolean>>;
26
26
  /**
27
27
  * Return a deep copy of a bare flow-definition body with server-echoed
28
28
  * noise removed: an empty `audience` and any detail-envelope keys. A
@@ -32,7 +32,7 @@ declare const USER_PROPERTY_DEFAULTS: Readonly<Record<string, boolean>>;
32
32
  * from responses, so comparing with it would read every scaffolded file
33
33
  * as permanently diverged.
34
34
  */
35
- declare function normalizeFlowBody(body: object): object;
35
+ export declare function normalizeFlowBody(body: object): object;
36
36
  /**
37
37
  * Return a deep copy of a user-schema body with meta-schema property
38
38
  * defaults stripped: a property carrying one of the
@@ -41,7 +41,6 @@ declare function normalizeFlowBody(body: object): object;
41
41
  * through untouched. Nested `properties` are descended into, so a leaf
42
42
  * of an object property normalizes the same way a top-level one does.
43
43
  */
44
- declare function normalizeSchemaBody(body: object): object;
44
+ export declare function normalizeSchemaBody(body: object): object;
45
45
  //#endregion
46
- export { USER_PROPERTY_DEFAULTS, normalizeFlowBody, normalizeSchemaBody };
47
46
  //# sourceMappingURL=normalize.d.mts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"normalize.d.mts","names":[],"sources":["../src/normalize.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;qBAwBa,wBAAwB,SAAS;;;;;;;;;;wBAwC9B,kBAAkB;;;;;;;;;wBAoBlB,oBAAoB"}
@@ -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-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 response 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\", \"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;CAAa;AAE3E,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"}
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 response 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\", \"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,MACb;;;;;;AAOA,MAAM,qBAAqB;CAAC;CAAM;CAAc;CAAc;AAAY;AAE1E,SAAS,cAAc,OAAkD;CACvE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;;;;AAOA,SAAS,gBAAgB,OAAyB;CAChD,IAAI,CAAC,cAAc,KAAK,GACtB,OAAO;CAET,OAAO,OAAO,QAAQ,KAAK,CAAC,CAAC,OAC1B,CAAC,KAAK,YACJ,QAAQ,cAAc,QAAQ,eAC9B,UAAU,QAAQ,UAAU,KAAA,KAAc,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,EACxF;AACF;;;;;;;;;;AAWA,SAAgB,kBAAkB,MAAsB;CACtD,MAAM,SAAS,gBAAgB,IAAI;CACnC,KAAK,MAAM,OAAO,oBAChB,OAAO,OAAO;CAEhB,OAAO,OAAO;CACd,IAAI,cAAc,UAAU,gBAAgB,OAAO,QAAQ,GACzD,OAAO,OAAO;CAEhB,OAAO;AACT;;;;;;;;;AAUA,SAAgB,oBAAoB,MAAsB;CACxD,MAAM,SAAS,gBAAgB,IAAI;CACnC,sBAAsB,MAAM;CAC5B,OAAO;AACT;;;;;;AAOA,SAAS,sBAAsB,QAAuC;CACpE,IAAI,CAAC,cAAc,OAAO,UAAU,GAClC;CAEF,KAAK,MAAM,YAAY,OAAO,OAAO,OAAO,UAAU,GAAG;EACvD,IAAI,CAAC,cAAc,QAAQ,GACzB;EAEF,KAAK,MAAM,CAAC,KAAK,iBAAiB,OAAO,QAAQ,sBAAsB,GACrE,IAAI,SAAS,SAAS,cACpB,OAAO,SAAS;EAGpB,sBAAsB,QAAQ;CAChC;AACF"}
@@ -1,11 +1,11 @@
1
1
  import { isCanonicalLoopbackHttpUrl } from "./branding-url.mjs";
2
- import { CreateBrandingBody, CreateFlowDefinitionBody, CreateSchemaBody } from "@zitadel/api/generated/endpoints/zitadelNextGen.zod";
2
+ import { CreateBrandingBody, CreateFlowDefinitionBody, CreateIdpBody, CreateSchemaBody } from "@zitadel/api/generated/endpoints/zitadelNextGen.zod";
3
3
  import { z } from "zod";
4
4
  /** Asset URL cap in UTF-8 bytes, like the server's `MaxBrandingURLBytes`. */
5
5
  const MAX_BRANDING_URL_BYTES = 2048;
6
6
  const HEX_COLOR = /^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
7
7
  /** `url` is absent by construction: it is not a colour function. */
8
- const COLOR_FUNCTIONS = new Set([
8
+ const COLOR_FUNCTIONS = /* @__PURE__ */ new Set([
9
9
  "rgb",
10
10
  "rgba",
11
11
  "hsl",
@@ -74,6 +74,13 @@ function isFontName(family) {
74
74
  const schemaConfigSchema = CreateSchemaBody;
75
75
  const flowConfigSchema = CreateFlowDefinitionBody.shape.flow_definition;
76
76
  const createFlowDefinitionRequestSchema = CreateFlowDefinitionBody;
77
+ /**
78
+ * One `.zitadel/idps/<slug>.json` body: the connection document `POST /idps`
79
+ * carries under `idp`. The generated Zod enforces the contract the meta-schema
80
+ * states, including that `client_secret` is a `${{ NAME }}` reference and
81
+ * never a literal.
82
+ */
83
+ const idpConnectionConfigSchema = CreateIdpBody.shape.idp;
77
84
  /** The wire shape of a branding revision (`POST /branding` request body). */
78
85
  const brandingWireSchema = CreateBrandingBody;
79
86
  /**
@@ -168,6 +175,6 @@ function validateBrandingAssetUrl(value, field, ctx, options = {}) {
168
175
  });
169
176
  }
170
177
  //#endregion
171
- export { localFileReferenceSchema as a, flowConfigSchema as i, brandingWireSchema as n, schemaConfigSchema as o, createFlowDefinitionRequestSchema as r, brandingConfigSchema as t };
178
+ export { idpConnectionConfigSchema as a, flowConfigSchema as i, brandingWireSchema as n, localFileReferenceSchema as o, createFlowDefinitionRequestSchema as r, schemaConfigSchema as s, brandingConfigSchema as t };
172
179
 
173
- //# sourceMappingURL=schemas-7oNU25Qz.mjs.map
180
+ //# sourceMappingURL=schemas-CUU4Rf8J.mjs.map