@zitadel/components 1.0.0-alpha.21 → 1.0.0-alpha.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/atoms/index.d.mts +1 -1
- package/dist/atoms/index.mjs +1 -1
- package/dist/{atoms-DSxjEHca.mjs → atoms-DbcPXAO0.mjs} +3 -2
- package/dist/atoms-DbcPXAO0.mjs.map +1 -0
- package/dist/{index-DFdR16-D.d.mts → index-1PzVwdYA.d.mts} +329 -100
- package/dist/index-1PzVwdYA.d.mts.map +1 -0
- package/dist/{index-Dx8rvpNs.d.mts → index-BRd9vg2q.d.mts} +12923 -6043
- package/dist/index-BRd9vg2q.d.mts.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +3 -3
- package/dist/manifests.mjs +1 -1
- package/dist/orchestrator/index.d.mts +2 -2
- package/dist/orchestrator/index.mjs +2 -2
- package/dist/{orchestrator-DG-YwFFE.mjs → orchestrator-BVmKaCqz.mjs} +699 -125
- package/dist/orchestrator-BVmKaCqz.mjs.map +1 -0
- package/dist/standalone.mjs +701 -126
- package/package.json +4 -4
- package/dist/atoms-DSxjEHca.mjs.map +0 -1
- package/dist/index-DFdR16-D.d.mts.map +0 -1
- package/dist/index-Dx8rvpNs.d.mts.map +0 -1
- package/dist/orchestrator-DG-YwFFE.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as focusVisibleStyles, E as emit, S as __decorate, T as t, d as hookName, w as baseHostStyles } from "./atoms-
|
|
2
|
-
import { i as tokensCss, t as THEME_SELECTORS } from "./tokens-T4N9VJz3.mjs";
|
|
1
|
+
import { C as focusVisibleStyles, E as emit, S as __decorate, T as t, d as hookName, w as baseHostStyles } from "./atoms-DbcPXAO0.mjs";
|
|
2
|
+
import { i as tokensCss, r as tokens, t as THEME_SELECTORS } from "./tokens-T4N9VJz3.mjs";
|
|
3
3
|
import { manifestRegistry } from "./manifests.mjs";
|
|
4
4
|
import { t as default_default } from "./default-Dnjq9iBt.mjs";
|
|
5
5
|
import { LitElement, css, html, nothing } from "lit";
|
|
@@ -331,18 +331,28 @@ function validateBranding(input, context = {}) {
|
|
|
331
331
|
out.layout = CreateFlow201BrandingLayout.centered;
|
|
332
332
|
}
|
|
333
333
|
out.logo_url = sanitiseUrl(out.logo_url, "logo_url", issues, { allowed: allowLoopbackHttp });
|
|
334
|
-
out.font_url
|
|
334
|
+
if (out.typography?.font_url) out.typography = {
|
|
335
|
+
...out.typography,
|
|
336
|
+
font_url: sanitiseUrl(out.typography.font_url, "typography.font_url", issues)
|
|
337
|
+
};
|
|
335
338
|
out.hero_url = sanitiseUrl(out.hero_url, "hero_url", issues, { allowed: allowLoopbackHttp });
|
|
336
|
-
if (out.
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
339
|
+
if (out.theme?.light || out.theme?.dark) out.theme = {
|
|
340
|
+
...out.theme,
|
|
341
|
+
...sanitiseSideLogo(out.theme.light, "light", issues, allowLoopbackHttp),
|
|
342
|
+
...sanitiseSideLogo(out.theme.dark, "dark", issues, allowLoopbackHttp)
|
|
340
343
|
};
|
|
341
344
|
return {
|
|
342
345
|
branding: out,
|
|
343
346
|
issues
|
|
344
347
|
};
|
|
345
348
|
}
|
|
349
|
+
function sanitiseSideLogo(side, name, issues, allowLoopbackHttp) {
|
|
350
|
+
if (!side) return {};
|
|
351
|
+
return { [name]: {
|
|
352
|
+
...side,
|
|
353
|
+
logo_url: sanitiseUrl(side.logo_url, `theme.${name}.logo_url`, issues, { allowed: allowLoopbackHttp })
|
|
354
|
+
} };
|
|
355
|
+
}
|
|
346
356
|
function sanitiseUrl(value, field, issues, loopbackHttp) {
|
|
347
357
|
if (value == null || value === "") return;
|
|
348
358
|
try {
|
|
@@ -360,6 +370,34 @@ function sanitiseUrl(value, field, issues, loopbackHttp) {
|
|
|
360
370
|
}
|
|
361
371
|
}
|
|
362
372
|
//#endregion
|
|
373
|
+
//#region src/orchestrator/branding.ts
|
|
374
|
+
/**
|
|
375
|
+
* Which sides the revision actually publishes. A side is published when the
|
|
376
|
+
* revision names it at all — an empty side object still says "this surface is
|
|
377
|
+
* mine", it just takes the maintained defaults for every key.
|
|
378
|
+
*/
|
|
379
|
+
function publishedSides(branding) {
|
|
380
|
+
const theme = branding?.theme;
|
|
381
|
+
const sides = [];
|
|
382
|
+
if (theme?.light) sides.push("light");
|
|
383
|
+
if (theme?.dark) sides.push("dark");
|
|
384
|
+
return sides;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* The mark for the resolved surface. A logo is pixels and is never recoloured,
|
|
388
|
+
* so a side without its own file shows no mark rather than borrowing the other
|
|
389
|
+
* side's. The legacy top-level `logo_url` is a single-mark fallback and applies
|
|
390
|
+
* only when neither side names one.
|
|
391
|
+
*/
|
|
392
|
+
function resolveLogoUrl(branding, theme) {
|
|
393
|
+
if (!branding) return void 0;
|
|
394
|
+
const sides = branding.theme;
|
|
395
|
+
const sideLogo = theme === "light" ? sides?.light?.logo_url : sides?.dark?.logo_url;
|
|
396
|
+
if (sideLogo) return sideLogo;
|
|
397
|
+
if (sides?.light?.logo_url || sides?.dark?.logo_url) return void 0;
|
|
398
|
+
return branding.logo_url;
|
|
399
|
+
}
|
|
400
|
+
//#endregion
|
|
363
401
|
//#region src/orchestrator/exportparts.ts
|
|
364
402
|
/**
|
|
365
403
|
* Forwards atom-internal CSS Shadow Parts through the orchestrator boundary.
|
|
@@ -816,7 +854,7 @@ const en = {
|
|
|
816
854
|
"password.action.register.lead": "Don't have an account? ",
|
|
817
855
|
"password.action.register.link": "Sign up",
|
|
818
856
|
"register.title": "Create your account",
|
|
819
|
-
"register.description": "",
|
|
857
|
+
"register.description": "Enter your details to get started",
|
|
820
858
|
"register.field.email": "Email",
|
|
821
859
|
"register.field.email.placeholder": "you@example.com",
|
|
822
860
|
"register.field.password": "Password",
|
|
@@ -864,7 +902,7 @@ const en = {
|
|
|
864
902
|
"error.password_required": "Please enter a password",
|
|
865
903
|
"error.password_incorrect": "Wrong email or password.",
|
|
866
904
|
"error.email_exists": "An account with this email already exists.",
|
|
867
|
-
/**
|
|
905
|
+
/** Sign-in error, inline on the password field. */
|
|
868
906
|
"error.invalid_credentials": "Wrong email or password.",
|
|
869
907
|
"error.required": "This field is required.",
|
|
870
908
|
"error.field_required": "{0} is required.",
|
|
@@ -933,7 +971,7 @@ const de = {
|
|
|
933
971
|
"password.action.register.lead": "Noch kein Konto? ",
|
|
934
972
|
"password.action.register.link": "Registrieren",
|
|
935
973
|
"register.title": "Konto erstellen",
|
|
936
|
-
"register.description": "",
|
|
974
|
+
"register.description": "Gib deine Daten ein, um loszulegen",
|
|
937
975
|
"register.field.email": "E-Mail",
|
|
938
976
|
"register.field.email.placeholder": "du@beispiel.de",
|
|
939
977
|
"register.field.password": "Passwort",
|
|
@@ -1049,7 +1087,7 @@ const it = {
|
|
|
1049
1087
|
"password.action.register.lead": "Non hai un account? ",
|
|
1050
1088
|
"password.action.register.link": "Registrati",
|
|
1051
1089
|
"register.title": "Crea il tuo account",
|
|
1052
|
-
"register.description": "",
|
|
1090
|
+
"register.description": "Inserisci i tuoi dati per iniziare",
|
|
1053
1091
|
"register.field.email": "E-mail",
|
|
1054
1092
|
"register.field.email.placeholder": "tu@esempio.com",
|
|
1055
1093
|
"register.field.password": "Password",
|
|
@@ -1187,22 +1225,24 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
|
|
|
1187
1225
|
createFlowDefinition: () => createFlowDefinition,
|
|
1188
1226
|
createGrant: () => createGrant,
|
|
1189
1227
|
createHandoff: () => createHandoff,
|
|
1228
|
+
createIdp: () => createIdp,
|
|
1190
1229
|
createProject: () => createProject,
|
|
1191
1230
|
createRelease: () => createRelease,
|
|
1192
1231
|
createSchema: () => createSchema,
|
|
1193
1232
|
createSession: () => createSession,
|
|
1194
1233
|
createTeam: () => createTeam,
|
|
1195
1234
|
createUser: () => createUser,
|
|
1196
|
-
deleteFlowDefinition: () => deleteFlowDefinition,
|
|
1197
1235
|
deleteGrant: () => deleteGrant,
|
|
1198
1236
|
deleteTeam: () => deleteTeam,
|
|
1199
1237
|
deleteUserByID: () => deleteUserByID,
|
|
1238
|
+
deleteVariable: () => deleteVariable,
|
|
1200
1239
|
exchangeHandoff: () => exchangeHandoff,
|
|
1201
1240
|
finishUserPasskeyRegistration: () => finishUserPasskeyRegistration,
|
|
1202
1241
|
getAuthAttempt: () => getAuthAttempt,
|
|
1203
1242
|
getBeginUserPasskeyRegistrationUrl: () => getBeginUserPasskeyRegistrationUrl,
|
|
1204
1243
|
getBrandingById: () => getBrandingById,
|
|
1205
1244
|
getClaimStatus: () => getClaimStatus,
|
|
1245
|
+
getClaimWindow: () => getClaimWindow,
|
|
1206
1246
|
getCompleteClaimUrl: () => getCompleteClaimUrl,
|
|
1207
1247
|
getCreateAuthAttemptUrl: () => getCreateAuthAttemptUrl,
|
|
1208
1248
|
getCreateBrandingUrl: () => getCreateBrandingUrl,
|
|
@@ -1210,16 +1250,17 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
|
|
|
1210
1250
|
getCreateFlowUrl: () => getCreateFlowUrl,
|
|
1211
1251
|
getCreateGrantUrl: () => getCreateGrantUrl,
|
|
1212
1252
|
getCreateHandoffUrl: () => getCreateHandoffUrl,
|
|
1253
|
+
getCreateIdpUrl: () => getCreateIdpUrl,
|
|
1213
1254
|
getCreateProjectUrl: () => getCreateProjectUrl,
|
|
1214
1255
|
getCreateReleaseUrl: () => getCreateReleaseUrl,
|
|
1215
1256
|
getCreateSchemaUrl: () => getCreateSchemaUrl,
|
|
1216
1257
|
getCreateSessionUrl: () => getCreateSessionUrl,
|
|
1217
1258
|
getCreateTeamUrl: () => getCreateTeamUrl,
|
|
1218
1259
|
getCreateUserUrl: () => getCreateUserUrl,
|
|
1219
|
-
getDeleteFlowDefinitionUrl: () => getDeleteFlowDefinitionUrl,
|
|
1220
1260
|
getDeleteGrantUrl: () => getDeleteGrantUrl,
|
|
1221
1261
|
getDeleteTeamUrl: () => getDeleteTeamUrl,
|
|
1222
1262
|
getDeleteUserByIDUrl: () => getDeleteUserByIDUrl,
|
|
1263
|
+
getDeleteVariableUrl: () => getDeleteVariableUrl,
|
|
1223
1264
|
getEnvironmentByName: () => getEnvironmentByName,
|
|
1224
1265
|
getEvent: () => getEvent,
|
|
1225
1266
|
getExchangeHandoffUrl: () => getExchangeHandoffUrl,
|
|
@@ -1229,12 +1270,15 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
|
|
|
1229
1270
|
getGetAuthAttemptUrl: () => getGetAuthAttemptUrl,
|
|
1230
1271
|
getGetBrandingByIdUrl: () => getGetBrandingByIdUrl,
|
|
1231
1272
|
getGetClaimStatusUrl: () => getGetClaimStatusUrl,
|
|
1273
|
+
getGetClaimWindowUrl: () => getGetClaimWindowUrl,
|
|
1232
1274
|
getGetEnvironmentByNameUrl: () => getGetEnvironmentByNameUrl,
|
|
1233
1275
|
getGetEventUrl: () => getGetEventUrl,
|
|
1234
1276
|
getGetFlowDefinitionUrl: () => getGetFlowDefinitionUrl,
|
|
1235
1277
|
getGetFlowStepUrl: () => getGetFlowStepUrl,
|
|
1236
1278
|
getGetGrantUrl: () => getGetGrantUrl,
|
|
1237
1279
|
getGetHealthUrl: () => getGetHealthUrl,
|
|
1280
|
+
getGetIdpByIdUrl: () => getGetIdpByIdUrl,
|
|
1281
|
+
getGetIdpRevisionByIdUrl: () => getGetIdpRevisionByIdUrl,
|
|
1238
1282
|
getGetLiveUrl: () => getGetLiveUrl,
|
|
1239
1283
|
getGetMySessionUrl: () => getGetMySessionUrl,
|
|
1240
1284
|
getGetMyUserUrl: () => getGetMyUserUrl,
|
|
@@ -1245,14 +1289,20 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
|
|
|
1245
1289
|
getGetSessionUrl: () => getGetSessionUrl,
|
|
1246
1290
|
getGetTeamUrl: () => getGetTeamUrl,
|
|
1247
1291
|
getGetUserByIDUrl: () => getGetUserByIDUrl,
|
|
1292
|
+
getGetVariableUrl: () => getGetVariableUrl,
|
|
1293
|
+
getGetVariablesUrl: () => getGetVariablesUrl,
|
|
1248
1294
|
getGrant: () => getGrant,
|
|
1249
1295
|
getHealth: () => getHealth,
|
|
1296
|
+
getIdpById: () => getIdpById,
|
|
1297
|
+
getIdpRevisionById: () => getIdpRevisionById,
|
|
1250
1298
|
getInitClaimUrl: () => getInitClaimUrl,
|
|
1251
1299
|
getIssueChallengeUrl: () => getIssueChallengeUrl,
|
|
1252
1300
|
getListBrandingUrl: () => getListBrandingUrl,
|
|
1253
1301
|
getListEnvironmentsUrl: () => getListEnvironmentsUrl,
|
|
1254
1302
|
getListEventsUrl: () => getListEventsUrl,
|
|
1255
1303
|
getListFlowDefinitionsUrl: () => getListFlowDefinitionsUrl,
|
|
1304
|
+
getListIdpRevisionsUrl: () => getListIdpRevisionsUrl,
|
|
1305
|
+
getListMyProjectsUrl: () => getListMyProjectsUrl,
|
|
1256
1306
|
getListReleasesUrl: () => getListReleasesUrl,
|
|
1257
1307
|
getListSchemasUrl: () => getListSchemasUrl,
|
|
1258
1308
|
getListUserPasskeysUrl: () => getListUserPasskeysUrl,
|
|
@@ -1260,8 +1310,12 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
|
|
|
1260
1310
|
getLive: () => getLive,
|
|
1261
1311
|
getMySession: () => getMySession,
|
|
1262
1312
|
getMyUser: () => getMyUser,
|
|
1313
|
+
getPatchMyUserUrl: () => getPatchMyUserUrl,
|
|
1263
1314
|
getPatchProjectUrl: () => getPatchProjectUrl,
|
|
1315
|
+
getPatchUserByIDUrl: () => getPatchUserByIDUrl,
|
|
1264
1316
|
getProject: () => getProject,
|
|
1317
|
+
getQueryGrantsUrl: () => getQueryGrantsUrl,
|
|
1318
|
+
getQueryIdpsUrl: () => getQueryIdpsUrl,
|
|
1265
1319
|
getQueryProjectsUrl: () => getQueryProjectsUrl,
|
|
1266
1320
|
getQuerySessionsUrl: () => getQuerySessionsUrl,
|
|
1267
1321
|
getQueryTeamsUrl: () => getQueryTeamsUrl,
|
|
@@ -1275,9 +1329,11 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
|
|
|
1275
1329
|
getSetUserPasswordUrl: () => getSetUserPasswordUrl,
|
|
1276
1330
|
getSubmitFlowStepUrl: () => getSubmitFlowStepUrl,
|
|
1277
1331
|
getTeam: () => getTeam,
|
|
1278
|
-
getUpdateFlowDefinitionUrl: () => getUpdateFlowDefinitionUrl,
|
|
1279
1332
|
getUpdateTeamUrl: () => getUpdateTeamUrl,
|
|
1333
|
+
getUpdateVariablesUrl: () => getUpdateVariablesUrl,
|
|
1280
1334
|
getUserByID: () => getUserByID,
|
|
1335
|
+
getVariable: () => getVariable,
|
|
1336
|
+
getVariables: () => getVariables,
|
|
1281
1337
|
getVerifyChallengeProofUrl: () => getVerifyChallengeProofUrl,
|
|
1282
1338
|
initClaim: () => initClaim,
|
|
1283
1339
|
issueChallenge: () => issueChallenge,
|
|
@@ -1285,11 +1341,17 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
|
|
|
1285
1341
|
listEnvironments: () => listEnvironments,
|
|
1286
1342
|
listEvents: () => listEvents,
|
|
1287
1343
|
listFlowDefinitions: () => listFlowDefinitions,
|
|
1344
|
+
listIdpRevisions: () => listIdpRevisions,
|
|
1345
|
+
listMyProjects: () => listMyProjects,
|
|
1288
1346
|
listReleases: () => listReleases,
|
|
1289
1347
|
listSchemas: () => listSchemas,
|
|
1290
1348
|
listUserPasskeys: () => listUserPasskeys,
|
|
1291
1349
|
listUserTeams: () => listUserTeams,
|
|
1350
|
+
patchMyUser: () => patchMyUser,
|
|
1292
1351
|
patchProject: () => patchProject,
|
|
1352
|
+
patchUserByID: () => patchUserByID,
|
|
1353
|
+
queryGrants: () => queryGrants,
|
|
1354
|
+
queryIdps: () => queryIdps,
|
|
1293
1355
|
queryProjects: () => queryProjects,
|
|
1294
1356
|
querySessions: () => querySessions,
|
|
1295
1357
|
queryTeams: () => queryTeams,
|
|
@@ -1298,8 +1360,8 @@ var zitadelNextGen_exports = /* @__PURE__ */ __exportAll({
|
|
|
1298
1360
|
revokeSession: () => revokeSession,
|
|
1299
1361
|
setUserPassword: () => setUserPassword,
|
|
1300
1362
|
submitFlowStep: () => submitFlowStep,
|
|
1301
|
-
updateFlowDefinition: () => updateFlowDefinition,
|
|
1302
1363
|
updateTeam: () => updateTeam,
|
|
1364
|
+
updateVariables: () => updateVariables,
|
|
1303
1365
|
verifyChallengeProof: () => verifyChallengeProof
|
|
1304
1366
|
});
|
|
1305
1367
|
const getGetHealthUrl = () => {
|
|
@@ -1369,9 +1431,14 @@ const getQueryUsersUrl = () => {
|
|
|
1369
1431
|
/**
|
|
1370
1432
|
* Returns the users of a project, paginated with a cursor.
|
|
1371
1433
|
|
|
1372
|
-
The project comes from the credential, not from a parameter: the
|
|
1373
|
-
is bound to the
|
|
1374
|
-
|
|
1434
|
+
The project comes from the credential, not from a parameter: the
|
|
1435
|
+
operation is bound to the credential's home project by construction
|
|
1436
|
+
(oauth2 secret or user-bound session). This is why it takes no
|
|
1437
|
+
`project_id`, unlike the other query endpoints.
|
|
1438
|
+
|
|
1439
|
+
Accepts either a project secret (`oauth2`) or a user-bound Console
|
|
1440
|
+
session cookie (`nextgenSession`). CSRF/Origin for cookie mutations
|
|
1441
|
+
is a follow-up (#1140).
|
|
1375
1442
|
|
|
1376
1443
|
* @summary Query users
|
|
1377
1444
|
*/
|
|
@@ -1403,6 +1470,28 @@ const getUserByID = async (userId, params, options) => {
|
|
|
1403
1470
|
method: "GET"
|
|
1404
1471
|
});
|
|
1405
1472
|
};
|
|
1473
|
+
const getPatchUserByIDUrl = (userId) => {
|
|
1474
|
+
return `${getProxyPath()}/users/${userId}`;
|
|
1475
|
+
};
|
|
1476
|
+
/**
|
|
1477
|
+
* Partially updates a user's schema-defined attributes, and optionally
|
|
1478
|
+
moves the user to another registered schema. The merged result is
|
|
1479
|
+
validated against the schema before commit. Concurrent writes are
|
|
1480
|
+
last-write-wins.
|
|
1481
|
+
|
|
1482
|
+
* @summary Update user by ID
|
|
1483
|
+
*/
|
|
1484
|
+
const patchUserByID = async (userId, patchUserByIDBody, options) => {
|
|
1485
|
+
return customFetch(getPatchUserByIDUrl(userId), {
|
|
1486
|
+
...options,
|
|
1487
|
+
method: "PATCH",
|
|
1488
|
+
headers: {
|
|
1489
|
+
"Content-Type": "application/json",
|
|
1490
|
+
...options?.headers
|
|
1491
|
+
},
|
|
1492
|
+
body: JSON.stringify(patchUserByIDBody)
|
|
1493
|
+
});
|
|
1494
|
+
};
|
|
1406
1495
|
const getDeleteUserByIDUrl = (userId) => {
|
|
1407
1496
|
return `${getProxyPath()}/users/${userId}`;
|
|
1408
1497
|
};
|
|
@@ -1533,6 +1622,52 @@ const getMyUser = async (options) => {
|
|
|
1533
1622
|
method: "GET"
|
|
1534
1623
|
});
|
|
1535
1624
|
};
|
|
1625
|
+
const getPatchMyUserUrl = () => {
|
|
1626
|
+
return `${getProxyPath()}/users/me`;
|
|
1627
|
+
};
|
|
1628
|
+
/**
|
|
1629
|
+
* Partially updates the caller's own schema-defined attributes. The merged
|
|
1630
|
+
result is validated against the user's schema before commit. Concurrent
|
|
1631
|
+
writes are last-write-wins.
|
|
1632
|
+
|
|
1633
|
+
* @summary Update my user information
|
|
1634
|
+
*/
|
|
1635
|
+
const patchMyUser = async (patchMyUserBody, options) => {
|
|
1636
|
+
return customFetch(getPatchMyUserUrl(), {
|
|
1637
|
+
...options,
|
|
1638
|
+
method: "PATCH",
|
|
1639
|
+
headers: {
|
|
1640
|
+
"Content-Type": "application/json",
|
|
1641
|
+
...options?.headers
|
|
1642
|
+
},
|
|
1643
|
+
body: JSON.stringify(patchMyUserBody)
|
|
1644
|
+
});
|
|
1645
|
+
};
|
|
1646
|
+
const getListMyProjectsUrl = (params) => {
|
|
1647
|
+
const normalizedParams = new URLSearchParams();
|
|
1648
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
1649
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
1650
|
+
});
|
|
1651
|
+
const stringifiedParams = normalizedParams.toString();
|
|
1652
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/users/me/projects?${stringifiedParams}` : `${getProxyPath()}/users/me/projects`;
|
|
1653
|
+
};
|
|
1654
|
+
/**
|
|
1655
|
+
* The projects the signed-in user holds an active grant on, either directly or
|
|
1656
|
+
through a team they are a member of. Ordered by project id and paged with an
|
|
1657
|
+
opaque cursor.
|
|
1658
|
+
|
|
1659
|
+
This is not the same question as `queryProjects`, which answers with the one
|
|
1660
|
+
project the calling credential is bound to. Here the session is the caller
|
|
1661
|
+
and the grants are the answer, so the list spans projects.
|
|
1662
|
+
|
|
1663
|
+
* @summary List the projects I can act on
|
|
1664
|
+
*/
|
|
1665
|
+
const listMyProjects = async (params, options) => {
|
|
1666
|
+
return customFetch(getListMyProjectsUrl(params), {
|
|
1667
|
+
...options,
|
|
1668
|
+
method: "GET"
|
|
1669
|
+
});
|
|
1670
|
+
};
|
|
1536
1671
|
const getCreateFlowUrl = () => {
|
|
1537
1672
|
return `${getProxyPath()}/flow`;
|
|
1538
1673
|
};
|
|
@@ -1839,7 +1974,11 @@ const getGetClaimStatusUrl = (projectId, params) => {
|
|
|
1839
1974
|
* Polled by the CLI while a browser completes the claim. Authorized by the
|
|
1840
1975
|
project secret that initiated the challenge. Returns `pending`, or
|
|
1841
1976
|
`completed` with the owning team, the claim timestamp, and the dashboard
|
|
1842
|
-
URL once the
|
|
1977
|
+
URL once the project is claimed. The claim grant, not the polled
|
|
1978
|
+
challenge, is the source of truth: a project claimed through another
|
|
1979
|
+
concurrent challenge also reports `completed`, and a completed claim
|
|
1980
|
+
keeps reporting `completed` past this challenge's expiry and past the
|
|
1981
|
+
project's claim window.
|
|
1843
1982
|
|
|
1844
1983
|
* @summary Get claim status
|
|
1845
1984
|
*/
|
|
@@ -1871,6 +2010,30 @@ const completeClaim = async (projectId, completeClaimBody, options) => {
|
|
|
1871
2010
|
body: JSON.stringify(completeClaimBody)
|
|
1872
2011
|
});
|
|
1873
2012
|
};
|
|
2013
|
+
const getGetClaimWindowUrl = (projectId, params) => {
|
|
2014
|
+
const normalizedParams = new URLSearchParams();
|
|
2015
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
2016
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
2017
|
+
});
|
|
2018
|
+
const stringifiedParams = normalizedParams.toString();
|
|
2019
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/projects/${projectId}/claim/window?${stringifiedParams}` : `${getProxyPath()}/projects/${projectId}/claim/window`;
|
|
2020
|
+
};
|
|
2021
|
+
/**
|
|
2022
|
+
* Read by the claim page in the browser to show how long is left to claim the
|
|
2023
|
+
project. Unauthenticated by design: the claim page runs this before the
|
|
2024
|
+
developer has signed in, and holding the `challenge_id` from the claim URL
|
|
2025
|
+
is the authorization — the same capability `claim/complete` accepts. It
|
|
2026
|
+
reveals only the window, never the project itself, and unlike
|
|
2027
|
+
`claim/complete` it spends nothing, so a reload is free.
|
|
2028
|
+
|
|
2029
|
+
* @summary Get the claim window for a project
|
|
2030
|
+
*/
|
|
2031
|
+
const getClaimWindow = async (projectId, params, options) => {
|
|
2032
|
+
return customFetch(getGetClaimWindowUrl(projectId, params), {
|
|
2033
|
+
...options,
|
|
2034
|
+
method: "GET"
|
|
2035
|
+
});
|
|
2036
|
+
};
|
|
1874
2037
|
const getCreateSessionUrl = () => {
|
|
1875
2038
|
return `${getProxyPath()}/sessions`;
|
|
1876
2039
|
};
|
|
@@ -2132,12 +2295,17 @@ const getCreateFlowDefinitionUrl = () => {
|
|
|
2132
2295
|
return `${getProxyPath()}/flow_definitions`;
|
|
2133
2296
|
};
|
|
2134
2297
|
/**
|
|
2135
|
-
*
|
|
2298
|
+
* Publishes a new flow definition revision.
|
|
2136
2299
|
Flow definitions are templates that define the sequence of steps (capabilities)
|
|
2137
2300
|
for a particular user journey (e.g., registration, login, password reset).
|
|
2138
2301
|
|
|
2139
2302
|
Flow definitions are created based on the flow definition schema, which includes the flow's purpose, audience, and the steps involved.
|
|
2140
2303
|
|
|
2304
|
+
Every call allocates a new opaque id. Revisions of one flow share its
|
|
2305
|
+
`name`; posting a definition under an existing `name` publishes a new
|
|
2306
|
+
revision of that flow, it is not a conflict. List with `name` to see a
|
|
2307
|
+
flow's revisions, newest by creation time first.
|
|
2308
|
+
|
|
2141
2309
|
* @summary Create a new flow definition
|
|
2142
2310
|
*/
|
|
2143
2311
|
const createFlowDefinition = async (createFlowDefinitionBody, options) => {
|
|
@@ -2160,8 +2328,9 @@ const getListFlowDefinitionsUrl = (params) => {
|
|
|
2160
2328
|
return stringifiedParams.length > 0 ? `${getProxyPath()}/flow_definitions?${stringifiedParams}` : `${getProxyPath()}/flow_definitions`;
|
|
2161
2329
|
};
|
|
2162
2330
|
/**
|
|
2163
|
-
* Retrieves a list of all flow definitions.
|
|
2331
|
+
* Retrieves a list of all flow definitions, newest by creation time first.
|
|
2164
2332
|
This endpoint can be used to view existing flow definitions and their configurations.
|
|
2333
|
+
Filter by `name` to list the revisions of one flow.
|
|
2165
2334
|
|
|
2166
2335
|
* @summary List flow definitions
|
|
2167
2336
|
*/
|
|
@@ -2184,42 +2353,6 @@ const getFlowDefinition = async (id, options) => {
|
|
|
2184
2353
|
method: "GET"
|
|
2185
2354
|
});
|
|
2186
2355
|
};
|
|
2187
|
-
const getUpdateFlowDefinitionUrl = (id) => {
|
|
2188
|
-
return `${getProxyPath()}/flow_definitions/${id}`;
|
|
2189
|
-
};
|
|
2190
|
-
/**
|
|
2191
|
-
* Update a flow definition by id. This endpoint replaces the existing flow definition.
|
|
2192
|
-
If `flow_definition.status` is omitted, the current status is preserved
|
|
2193
|
-
|
|
2194
|
-
* @summary Update a flow definition by id
|
|
2195
|
-
*/
|
|
2196
|
-
const updateFlowDefinition = async (id, updateFlowDefinitionBody, options) => {
|
|
2197
|
-
return customFetch(getUpdateFlowDefinitionUrl(id), {
|
|
2198
|
-
...options,
|
|
2199
|
-
method: "PUT",
|
|
2200
|
-
headers: {
|
|
2201
|
-
"Content-Type": "application/json",
|
|
2202
|
-
...options?.headers
|
|
2203
|
-
},
|
|
2204
|
-
body: JSON.stringify(updateFlowDefinitionBody)
|
|
2205
|
-
});
|
|
2206
|
-
};
|
|
2207
|
-
const getDeleteFlowDefinitionUrl = (id) => {
|
|
2208
|
-
return `${getProxyPath()}/flow_definitions/${id}`;
|
|
2209
|
-
};
|
|
2210
|
-
/**
|
|
2211
|
-
* Delete a flow definition by id.
|
|
2212
|
-
If the flow definition is currently being used by a flow, the deletion will fail.
|
|
2213
|
-
If the flow definition is the last active flow definition for a given purpose, the deletion will fail to prevent disruption of new flows being started for that purpose.
|
|
2214
|
-
|
|
2215
|
-
* @summary Delete a flow definition by id
|
|
2216
|
-
*/
|
|
2217
|
-
const deleteFlowDefinition = async (id, options) => {
|
|
2218
|
-
return customFetch(getDeleteFlowDefinitionUrl(id), {
|
|
2219
|
-
...options,
|
|
2220
|
-
method: "DELETE"
|
|
2221
|
-
});
|
|
2222
|
-
};
|
|
2223
2356
|
const getCreateTeamUrl = (params) => {
|
|
2224
2357
|
const normalizedParams = new URLSearchParams();
|
|
2225
2358
|
Object.entries(params || {}).forEach(([key, value]) => {
|
|
@@ -2293,6 +2426,11 @@ users whose lifecycle it owns are deactivated with it.
|
|
|
2293
2426
|
The request is idempotent. Deleting a team that is already deactivated
|
|
2294
2427
|
or doesn't exist succeeds without changing anything.
|
|
2295
2428
|
|
|
2429
|
+
A team that still owns a project is refused with `409
|
|
2430
|
+
team.owns_project`: deactivating it would leave the project owned by a
|
|
2431
|
+
dead team and so unmanageable. No endpoint releases that ownership yet,
|
|
2432
|
+
so such a team cannot currently be deactivated through the API.
|
|
2433
|
+
|
|
2296
2434
|
* @summary Delete team
|
|
2297
2435
|
*/
|
|
2298
2436
|
const deleteTeam = async (teamId, options) => {
|
|
@@ -2372,8 +2510,7 @@ const getCreateBrandingUrl = (params) => {
|
|
|
2372
2510
|
/**
|
|
2373
2511
|
* Publishes a new immutable branding revision for the project. Branding
|
|
2374
2512
|
revisions cannot be updated or deleted; every edit publishes a new
|
|
2375
|
-
revision, and flow responses resolve the latest revision per project
|
|
2376
|
-
(see ADR 040).
|
|
2513
|
+
revision, and flow responses resolve the latest revision per project.
|
|
2377
2514
|
|
|
2378
2515
|
The `liquid_template` is validated lexically on save (size, encoding,
|
|
2379
2516
|
banned patterns such as `<script>` tags, inline event handlers, and the
|
|
@@ -2404,9 +2541,9 @@ const getListBrandingUrl = (params) => {
|
|
|
2404
2541
|
/**
|
|
2405
2542
|
* Lists branding revisions for the project, newest first, capped at the
|
|
2406
2543
|
100 most recent. The first entry is the revision flow responses
|
|
2407
|
-
currently resolve. Deliberately unpaginated in v1 — list endpoints
|
|
2408
|
-
|
|
2409
|
-
|
|
2544
|
+
currently resolve. Deliberately unpaginated in v1 — list endpoints gain a
|
|
2545
|
+
real query mechanism together; advertising pagination parameters the
|
|
2546
|
+
server ignores would be worse than none.
|
|
2410
2547
|
|
|
2411
2548
|
* @summary List branding revisions
|
|
2412
2549
|
*/
|
|
@@ -2429,6 +2566,142 @@ const getBrandingById = async (id, options) => {
|
|
|
2429
2566
|
method: "GET"
|
|
2430
2567
|
});
|
|
2431
2568
|
};
|
|
2569
|
+
const getCreateIdpUrl = (params) => {
|
|
2570
|
+
const normalizedParams = new URLSearchParams();
|
|
2571
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
2572
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
2573
|
+
});
|
|
2574
|
+
const stringifiedParams = normalizedParams.toString();
|
|
2575
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/idps?${stringifiedParams}` : `${getProxyPath()}/idps`;
|
|
2576
|
+
};
|
|
2577
|
+
/**
|
|
2578
|
+
* Creates or revises a connection document. If the slug does not already
|
|
2579
|
+
exist, a new connection is created. If a connection with that slug already
|
|
2580
|
+
exists, a revision is created.
|
|
2581
|
+
|
|
2582
|
+
A revision gets a new `revision_id` and does not modify the connection
|
|
2583
|
+
`id`, so identity links that reference the connection keep resolving while
|
|
2584
|
+
releases and auth attempts stay pinned to the revision they captured.
|
|
2585
|
+
|
|
2586
|
+
Identity fields are fixed for the life of the connection and a revision
|
|
2587
|
+
that changes one is rejected: `protocol`, `subject_claim`, and the
|
|
2588
|
+
endpoints that name the authority (`issuer` for OIDC, `token_endpoint`
|
|
2589
|
+
and `userinfo_endpoint` for OAuth 2.0). Their values decide which provider
|
|
2590
|
+
account a stored subject belongs to, so changing one would silently
|
|
2591
|
+
repoint existing identities at a different provider.
|
|
2592
|
+
|
|
2593
|
+
`subject_claim` is optional for OIDC and required for OAuth 2.0. For an
|
|
2594
|
+
OIDC connection, `sub` is used as the default value. If a revision updates
|
|
2595
|
+
`subject_claim` to a different value than the one set during creation, the
|
|
2596
|
+
request is rejected.
|
|
2597
|
+
|
|
2598
|
+
The document is validated against the `idp-connection.json` schema before
|
|
2599
|
+
anything is stored.
|
|
2600
|
+
|
|
2601
|
+
* @summary Create or revise an identity provider connection
|
|
2602
|
+
*/
|
|
2603
|
+
const createIdp = async (createIdpBody, params, options) => {
|
|
2604
|
+
return customFetch(getCreateIdpUrl(params), {
|
|
2605
|
+
...options,
|
|
2606
|
+
method: "POST",
|
|
2607
|
+
headers: {
|
|
2608
|
+
"Content-Type": "application/json",
|
|
2609
|
+
...options?.headers
|
|
2610
|
+
},
|
|
2611
|
+
body: JSON.stringify(createIdpBody)
|
|
2612
|
+
});
|
|
2613
|
+
};
|
|
2614
|
+
const getQueryIdpsUrl = (params) => {
|
|
2615
|
+
const normalizedParams = new URLSearchParams();
|
|
2616
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
2617
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
2618
|
+
});
|
|
2619
|
+
const stringifiedParams = normalizedParams.toString();
|
|
2620
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/idps/query?${stringifiedParams}` : `${getProxyPath()}/idps/query`;
|
|
2621
|
+
};
|
|
2622
|
+
/**
|
|
2623
|
+
* Returns the identity provider connections of a project, paginated with a
|
|
2624
|
+
cursor. One row per connection, carrying its newest revision.
|
|
2625
|
+
|
|
2626
|
+
* @summary Query identity provider connections
|
|
2627
|
+
*/
|
|
2628
|
+
const queryIdps = async (queryIdpsBody, params, options) => {
|
|
2629
|
+
return customFetch(getQueryIdpsUrl(params), {
|
|
2630
|
+
...options,
|
|
2631
|
+
method: "POST",
|
|
2632
|
+
headers: {
|
|
2633
|
+
"Content-Type": "application/json",
|
|
2634
|
+
...options?.headers
|
|
2635
|
+
},
|
|
2636
|
+
body: JSON.stringify(queryIdpsBody)
|
|
2637
|
+
});
|
|
2638
|
+
};
|
|
2639
|
+
const getGetIdpByIdUrl = (id, params) => {
|
|
2640
|
+
const normalizedParams = new URLSearchParams();
|
|
2641
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
2642
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
2643
|
+
});
|
|
2644
|
+
const stringifiedParams = normalizedParams.toString();
|
|
2645
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/idps/${id}?${stringifiedParams}` : `${getProxyPath()}/idps/${id}`;
|
|
2646
|
+
};
|
|
2647
|
+
/**
|
|
2648
|
+
* Reads a connection by its id, at its newest revision.
|
|
2649
|
+
`GET /idps/{id}/revisions` lists every revision of the connection.
|
|
2650
|
+
|
|
2651
|
+
The lookup is scoped to the project in `project_id`.
|
|
2652
|
+
|
|
2653
|
+
* @summary Get an identity provider connection by id
|
|
2654
|
+
*/
|
|
2655
|
+
const getIdpById = async (id, params, options) => {
|
|
2656
|
+
return customFetch(getGetIdpByIdUrl(id, params), {
|
|
2657
|
+
...options,
|
|
2658
|
+
method: "GET"
|
|
2659
|
+
});
|
|
2660
|
+
};
|
|
2661
|
+
const getListIdpRevisionsUrl = (id, params) => {
|
|
2662
|
+
const normalizedParams = new URLSearchParams();
|
|
2663
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
2664
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
2665
|
+
});
|
|
2666
|
+
const stringifiedParams = normalizedParams.toString();
|
|
2667
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/idps/${id}/revisions?${stringifiedParams}` : `${getProxyPath()}/idps/${id}/revisions`;
|
|
2668
|
+
};
|
|
2669
|
+
/**
|
|
2670
|
+
* Returns every revision of one connection, newest first, paginated with a
|
|
2671
|
+
cursor. The order is fixed, so there is no `sorting`.
|
|
2672
|
+
|
|
2673
|
+
The lookup is scoped to the project in `project_id`.
|
|
2674
|
+
|
|
2675
|
+
* @summary List the revisions of an identity provider connection
|
|
2676
|
+
*/
|
|
2677
|
+
const listIdpRevisions = async (id, params, options) => {
|
|
2678
|
+
return customFetch(getListIdpRevisionsUrl(id, params), {
|
|
2679
|
+
...options,
|
|
2680
|
+
method: "GET"
|
|
2681
|
+
});
|
|
2682
|
+
};
|
|
2683
|
+
const getGetIdpRevisionByIdUrl = (revisionId, params) => {
|
|
2684
|
+
const normalizedParams = new URLSearchParams();
|
|
2685
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
2686
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
2687
|
+
});
|
|
2688
|
+
const stringifiedParams = normalizedParams.toString();
|
|
2689
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/idps/revisions/${revisionId}?${stringifiedParams}` : `${getProxyPath()}/idps/revisions/${revisionId}`;
|
|
2690
|
+
};
|
|
2691
|
+
/**
|
|
2692
|
+
* Reads one revision of a connection by its `revision_id`, the value an auth
|
|
2693
|
+
attempt or a release pins.
|
|
2694
|
+
|
|
2695
|
+
The lookup is scoped to the project in `project_id`.
|
|
2696
|
+
|
|
2697
|
+
* @summary Get one revision of an identity provider connection
|
|
2698
|
+
*/
|
|
2699
|
+
const getIdpRevisionById = async (revisionId, params, options) => {
|
|
2700
|
+
return customFetch(getGetIdpRevisionByIdUrl(revisionId, params), {
|
|
2701
|
+
...options,
|
|
2702
|
+
method: "GET"
|
|
2703
|
+
});
|
|
2704
|
+
};
|
|
2432
2705
|
const getCreateReleaseUrl = (params) => {
|
|
2433
2706
|
const normalizedParams = new URLSearchParams();
|
|
2434
2707
|
Object.entries(params || {}).forEach(([key, value]) => {
|
|
@@ -2528,10 +2801,23 @@ const getCreateGrantUrl = (params) => {
|
|
|
2528
2801
|
/**
|
|
2529
2802
|
* Bind a user or team to `project.viewer`, `project.editor`, or
|
|
2530
2803
|
`project.admin` on the project identified by the `project-id` header.
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2804
|
+
Name the principal with `user` (`user_id` or `identifier`) or `team`
|
|
2805
|
+
(`team_id` or `name`). IDs are `asgn_<opaque>`. Owning-team
|
|
2806
|
+
(`project.team`) grants are not created here — claim owns that path. An
|
|
2807
|
+
unrevoked grant with the same principal and relation occupies the unique
|
|
2808
|
+
key even after `expires_at`; DELETE it before re-creating.
|
|
2809
|
+
Create does not accept `expand`. The `user_id` and team locators
|
|
2810
|
+
return 201, whose `user` / `team` carry only `user_id` / `team_id`,
|
|
2811
|
+
and still 404 / 409 when the principal is missing or the tuple
|
|
2812
|
+
already exists. Creating by `user.identifier` answers 202 with no
|
|
2813
|
+
body on every outcome. Granting the session caller's own user is
|
|
2814
|
+
`grant.invalid` on either user locator. Read the grant back for
|
|
2815
|
+
identifier and display.
|
|
2816
|
+
|
|
2817
|
+
Accepts either a project secret (`oauth2`) or a user-bound Console
|
|
2818
|
+
session cookie (`nextgenSession`). Session callers are authorized as
|
|
2819
|
+
the human against the target project (home may differ). CSRF/Origin
|
|
2820
|
+
for cookie mutations is a follow-up (#1140).
|
|
2535
2821
|
|
|
2536
2822
|
* @summary Create grant
|
|
2537
2823
|
*/
|
|
@@ -2546,6 +2832,45 @@ const createGrant = async (createGrantBody, params, options) => {
|
|
|
2546
2832
|
body: JSON.stringify(createGrantBody)
|
|
2547
2833
|
});
|
|
2548
2834
|
};
|
|
2835
|
+
const getQueryGrantsUrl = (params) => {
|
|
2836
|
+
const normalizedParams = new URLSearchParams();
|
|
2837
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
2838
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
2839
|
+
});
|
|
2840
|
+
const stringifiedParams = normalizedParams.toString();
|
|
2841
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/grants/query?${stringifiedParams}` : `${getProxyPath()}/grants/query`;
|
|
2842
|
+
};
|
|
2843
|
+
/**
|
|
2844
|
+
* Returns the collaboration grants of a project, paginated with a cursor.
|
|
2845
|
+
Only unrevoked grants this API manages are listed (user or team bound to
|
|
2846
|
+
viewer, editor, or admin), including expired grants so a client can
|
|
2847
|
+
DELETE before re-granting. Project-secret setup (`sk_proj`) and
|
|
2848
|
+
owning-team (`relation=team`) rows are not returned. Grants are not in
|
|
2849
|
+
`resource_scope_index`; project scope is required on the query (same as
|
|
2850
|
+
get). Requires `project.read`. `expand: ["principal"]` adds envelope
|
|
2851
|
+
fields on `user` / `team` and additionally requires `user.read` and
|
|
2852
|
+
`team.read` for project secrets (documented on the expand enum; those
|
|
2853
|
+
scopes cannot be ANDed onto this security block because they are
|
|
2854
|
+
body-conditional). A user-bound Console session that already passed
|
|
2855
|
+
the project Check may expand without those scopes.
|
|
2856
|
+
|
|
2857
|
+
Accepts either a project secret (`oauth2`) or a user-bound Console
|
|
2858
|
+
session cookie (`nextgenSession`). CSRF/Origin for cookie mutations
|
|
2859
|
+
is a follow-up (#1140).
|
|
2860
|
+
|
|
2861
|
+
* @summary Query grants
|
|
2862
|
+
*/
|
|
2863
|
+
const queryGrants = async (queryGrantsBody, params, options) => {
|
|
2864
|
+
return customFetch(getQueryGrantsUrl(params), {
|
|
2865
|
+
...options,
|
|
2866
|
+
method: "POST",
|
|
2867
|
+
headers: {
|
|
2868
|
+
"Content-Type": "application/json",
|
|
2869
|
+
...options?.headers
|
|
2870
|
+
},
|
|
2871
|
+
body: JSON.stringify(queryGrantsBody)
|
|
2872
|
+
});
|
|
2873
|
+
};
|
|
2549
2874
|
const getGetGrantUrl = (id, params) => {
|
|
2550
2875
|
const normalizedParams = new URLSearchParams();
|
|
2551
2876
|
Object.entries(params || {}).forEach(([key, value]) => {
|
|
@@ -2563,6 +2888,14 @@ still ignores expired grants. Grants are not registered in
|
|
|
2563
2888
|
`resource_scope_index`; project scope is required on the query (same as
|
|
2564
2889
|
events). Misses, revoked rows, project-secret setup (`sk_proj`),
|
|
2565
2890
|
owning-team (`relation=team`) rows, and cross-project ids return 404.
|
|
2891
|
+
`expand=principal` adds envelope fields on `user` or `team` and requires
|
|
2892
|
+
`user.read` and `team.read` in addition to `project.read` for project
|
|
2893
|
+
secrets. A user-bound Console session that already passed the project
|
|
2894
|
+
Check may expand without those scopes.
|
|
2895
|
+
|
|
2896
|
+
Accepts either a project secret (`oauth2`) or a user-bound Console
|
|
2897
|
+
session cookie (`nextgenSession`). CSRF/Origin for cookie mutations
|
|
2898
|
+
is a follow-up (#1140).
|
|
2566
2899
|
|
|
2567
2900
|
* @summary Get grant
|
|
2568
2901
|
*/
|
|
@@ -2586,6 +2919,10 @@ Already-revoked, missing, project-secret setup, and owning-team grants
|
|
|
2586
2919
|
return 404. The row is not un-revoked. Expired grants can still be
|
|
2587
2920
|
revoked so the unique binding can be reused.
|
|
2588
2921
|
|
|
2922
|
+
Accepts either a project secret (`oauth2`) or a user-bound Console
|
|
2923
|
+
session cookie (`nextgenSession`). CSRF/Origin for cookie mutations
|
|
2924
|
+
is a follow-up (#1140).
|
|
2925
|
+
|
|
2589
2926
|
* @summary Revoke grant
|
|
2590
2927
|
*/
|
|
2591
2928
|
const deleteGrant = async (id, params, options) => {
|
|
@@ -2652,6 +2989,122 @@ const getEvent = async (id, params, options) => {
|
|
|
2652
2989
|
method: "GET"
|
|
2653
2990
|
});
|
|
2654
2991
|
};
|
|
2992
|
+
const getGetVariablesUrl = (params) => {
|
|
2993
|
+
const normalizedParams = new URLSearchParams();
|
|
2994
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
2995
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
2996
|
+
});
|
|
2997
|
+
const stringifiedParams = normalizedParams.toString();
|
|
2998
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/variables?${stringifiedParams}` : `${getProxyPath()}/variables`;
|
|
2999
|
+
};
|
|
3000
|
+
/**
|
|
3001
|
+
* Returns the variables entered at the owner this request addresses, keyed by
|
|
3002
|
+
name — one environment of the project with `environment_name`, the project
|
|
3003
|
+
level itself without it.
|
|
3004
|
+
|
|
3005
|
+
Owners are separate, not a ladder: an environment does not inherit the
|
|
3006
|
+
project's variables and the project does not see its environments'. Reading
|
|
3007
|
+
everything a project holds therefore means reading each owner in turn.
|
|
3008
|
+
|
|
3009
|
+
Secret values are not returned. A secret appears as `{"secret": true}`,
|
|
3010
|
+
which says a value is held without disclosing it.
|
|
3011
|
+
|
|
3012
|
+
* @summary Get variables
|
|
3013
|
+
*/
|
|
3014
|
+
const getVariables = async (params, options) => {
|
|
3015
|
+
return customFetch(getGetVariablesUrl(params), {
|
|
3016
|
+
...options,
|
|
3017
|
+
method: "GET"
|
|
3018
|
+
});
|
|
3019
|
+
};
|
|
3020
|
+
const getUpdateVariablesUrl = (params) => {
|
|
3021
|
+
const normalizedParams = new URLSearchParams();
|
|
3022
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
3023
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
3024
|
+
});
|
|
3025
|
+
const stringifiedParams = normalizedParams.toString();
|
|
3026
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/variables?${stringifiedParams}` : `${getProxyPath()}/variables`;
|
|
3027
|
+
};
|
|
3028
|
+
/**
|
|
3029
|
+
* Enters, replaces and removes variables at the owner this request
|
|
3030
|
+
addresses.
|
|
3031
|
+
|
|
3032
|
+
Every name in the body is applied at exactly that owner — the project, or
|
|
3033
|
+
the environment named by `environment_name` — and reaches no other. Names
|
|
3034
|
+
not in the body are untouched.
|
|
3035
|
+
|
|
3036
|
+
A bare scalar enters a non-secret value. `{"value": …, "secret": true}`
|
|
3037
|
+
stores the value encrypted under the project's active `secret` key, after
|
|
3038
|
+
which it can be referenced but not read back. `null` removes the name from
|
|
3039
|
+
this owner (RFC 7386), which is how several variables are removed in one
|
|
3040
|
+
request; removing a name this owner does not hold is a no-op rather than an
|
|
3041
|
+
error.
|
|
3042
|
+
|
|
3043
|
+
The body is applied whole or not at all, so a rejected request leaves the
|
|
3044
|
+
owner exactly as it was. Writing the same name and owner twice replaces the
|
|
3045
|
+
value rather than duplicating it, which makes a retry safe.
|
|
3046
|
+
|
|
3047
|
+
* @summary Update variables
|
|
3048
|
+
*/
|
|
3049
|
+
const updateVariables = async (updateVariablesBody, params, options) => {
|
|
3050
|
+
return customFetch(getUpdateVariablesUrl(params), {
|
|
3051
|
+
...options,
|
|
3052
|
+
method: "PATCH",
|
|
3053
|
+
headers: {
|
|
3054
|
+
"Content-Type": "application/json",
|
|
3055
|
+
...options?.headers
|
|
3056
|
+
},
|
|
3057
|
+
body: JSON.stringify(updateVariablesBody)
|
|
3058
|
+
});
|
|
3059
|
+
};
|
|
3060
|
+
const getGetVariableUrl = (variableName, params) => {
|
|
3061
|
+
const normalizedParams = new URLSearchParams();
|
|
3062
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
3063
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
3064
|
+
});
|
|
3065
|
+
const stringifiedParams = normalizedParams.toString();
|
|
3066
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/variables/${variableName}?${stringifiedParams}` : `${getProxyPath()}/variables/${variableName}`;
|
|
3067
|
+
};
|
|
3068
|
+
/**
|
|
3069
|
+
* Reads one variable by name from the owner this request addresses — one
|
|
3070
|
+
environment of the project with `environment_name`, the project level
|
|
3071
|
+
itself without it.
|
|
3072
|
+
|
|
3073
|
+
A name that owner has not entered answers `var.not_found`, even when
|
|
3074
|
+
another owner of the same project holds it: nothing is inherited. A secret
|
|
3075
|
+
is found but not disclosed: the response is `{"secret": true}`.
|
|
3076
|
+
|
|
3077
|
+
* @summary Get a variable
|
|
3078
|
+
*/
|
|
3079
|
+
const getVariable = async (variableName, params, options) => {
|
|
3080
|
+
return customFetch(getGetVariableUrl(variableName, params), {
|
|
3081
|
+
...options,
|
|
3082
|
+
method: "GET"
|
|
3083
|
+
});
|
|
3084
|
+
};
|
|
3085
|
+
const getDeleteVariableUrl = (variableName, params) => {
|
|
3086
|
+
const normalizedParams = new URLSearchParams();
|
|
3087
|
+
Object.entries(params || {}).forEach(([key, value]) => {
|
|
3088
|
+
if (value !== void 0) normalizedParams.append(key, value === null ? "null" : value.toString());
|
|
3089
|
+
});
|
|
3090
|
+
const stringifiedParams = normalizedParams.toString();
|
|
3091
|
+
return stringifiedParams.length > 0 ? `${getProxyPath()}/variables/${variableName}?${stringifiedParams}` : `${getProxyPath()}/variables/${variableName}`;
|
|
3092
|
+
};
|
|
3093
|
+
/**
|
|
3094
|
+
* Removes the variable this owner entered under this name.
|
|
3095
|
+
|
|
3096
|
+
A variable is deletable only by the owner that entered it. Deleting a name
|
|
3097
|
+
another owner of the same project holds answers `var.not_found` and leaves
|
|
3098
|
+
that owner's value standing.
|
|
3099
|
+
|
|
3100
|
+
* @summary Delete a variable
|
|
3101
|
+
*/
|
|
3102
|
+
const deleteVariable = async (variableName, params, options) => {
|
|
3103
|
+
return customFetch(getDeleteVariableUrl(variableName, params), {
|
|
3104
|
+
...options,
|
|
3105
|
+
method: "DELETE"
|
|
3106
|
+
});
|
|
3107
|
+
};
|
|
2655
3108
|
//#endregion
|
|
2656
3109
|
//#region ../api/dist/runtime/api-factory.mjs
|
|
2657
3110
|
/**
|
|
@@ -2967,9 +3420,10 @@ function createSanitiser() {
|
|
|
2967
3420
|
* the `Branding` JSON into a CSSStyleSheet of `:host { --zl-* }` declarations
|
|
2968
3421
|
* and applies it via `shadowRoot.adoptedStyleSheets`.
|
|
2969
3422
|
*
|
|
2970
|
-
*
|
|
2971
|
-
*
|
|
2972
|
-
*
|
|
3423
|
+
* Light and dark are independent surfaces: each side's palette is emitted under
|
|
3424
|
+
* its own `data-theme` selector only, so a key the revision left unset on one
|
|
3425
|
+
* side takes the maintained default for that side rather than the other side's
|
|
3426
|
+
* colour. Resolution between `light | dark | auto` happens in `<zitadel-login>`.
|
|
2973
3427
|
*
|
|
2974
3428
|
* Base layer: the design-tokens package ships the full `--zl-*` set as both a
|
|
2975
3429
|
* `.css` file (for host pages) and a `tokensCss` string (for shadow roots and
|
|
@@ -2977,13 +3431,32 @@ function createSanitiser() {
|
|
|
2977
3431
|
* paint correctly even when the host page didn't `@import` tokens.css — the
|
|
2978
3432
|
* orchestrator is meant to drop into any page.
|
|
2979
3433
|
*/
|
|
2980
|
-
const
|
|
2981
|
-
|
|
2982
|
-
sm
|
|
2983
|
-
md
|
|
2984
|
-
lg
|
|
2985
|
-
|
|
2986
|
-
|
|
3434
|
+
const RADIUS_STEPS = [
|
|
3435
|
+
"xs",
|
|
3436
|
+
"sm",
|
|
3437
|
+
"md",
|
|
3438
|
+
"lg",
|
|
3439
|
+
"xl"
|
|
3440
|
+
];
|
|
3441
|
+
const RADIUS_RATIOS = (() => {
|
|
3442
|
+
const base = remToNumber(tokens.radius.md);
|
|
3443
|
+
const ratios = {};
|
|
3444
|
+
for (const step of RADIUS_STEPS) ratios[step] = remToNumber(tokens.radius[step]) / base;
|
|
3445
|
+
return ratios;
|
|
3446
|
+
})();
|
|
3447
|
+
const RADIUS_PRESETS = {
|
|
3448
|
+
none: 0,
|
|
3449
|
+
sm: .25,
|
|
3450
|
+
md: .5,
|
|
3451
|
+
lg: .75
|
|
3452
|
+
};
|
|
3453
|
+
const TEXT_STEPS = [
|
|
3454
|
+
"xs",
|
|
3455
|
+
"sm",
|
|
3456
|
+
"base",
|
|
3457
|
+
"lg",
|
|
3458
|
+
"xl"
|
|
3459
|
+
];
|
|
2987
3460
|
const DENSITY_MAP = {
|
|
2988
3461
|
compact: {
|
|
2989
3462
|
"--zl-spacing-4": "0.75rem",
|
|
@@ -3035,34 +3508,35 @@ const PALETTE_MAP = {
|
|
|
3035
3508
|
* the moment before a theme resolves.
|
|
3036
3509
|
*/
|
|
3037
3510
|
const BRANDING_SELECTOR = ":host, :host([data-theme=\"light\"]), :host([data-theme=\"dark\"])";
|
|
3038
|
-
/**
|
|
3511
|
+
/** Per-side palettes. Emitted after {@link BRANDING_SELECTOR}, so they win on order. */
|
|
3512
|
+
const LIGHT_SELECTOR = ":host([data-theme=\"light\"])";
|
|
3039
3513
|
const DARK_SELECTOR = ":host([data-theme=\"dark\"])";
|
|
3040
3514
|
/**
|
|
3041
3515
|
* Build a CSS string of `:host { --zl-* }` declarations from a Branding
|
|
3042
|
-
* payload.
|
|
3043
|
-
*
|
|
3516
|
+
* payload. Shape and typography are shared across the sides and land on the
|
|
3517
|
+
* shared selector; each side's palette lands only under its own `data-theme`
|
|
3518
|
+
* selector, and nowhere else.
|
|
3519
|
+
*
|
|
3520
|
+
* No copy of the resolved side goes on the shared selector. That copy would
|
|
3521
|
+
* reach `[data-theme="light"]` as well, so a key set on the dark side and not
|
|
3522
|
+
* the light one would paint on both, with nothing in the light block to
|
|
3523
|
+
* override it. Callers stamp `data-theme` on the host — `applySurfaceTheme`
|
|
3524
|
+
* does it in the same update that adopts this sheet — so the per-side block is
|
|
3525
|
+
* already matching by the time anything paints.
|
|
3044
3526
|
*/
|
|
3045
|
-
function buildBrandingStylesheet(branding
|
|
3046
|
-
const
|
|
3047
|
-
const
|
|
3048
|
-
const
|
|
3527
|
+
function buildBrandingStylesheet(branding) {
|
|
3528
|
+
const shared = collectDeclarations(branding);
|
|
3529
|
+
const light = mapPalette(branding?.theme?.light?.palette);
|
|
3530
|
+
const dark = mapPalette(branding?.theme?.dark?.palette);
|
|
3049
3531
|
const blocks = [];
|
|
3050
|
-
if (Object.keys(
|
|
3051
|
-
if (Object.keys(
|
|
3052
|
-
if (
|
|
3053
|
-
const merged = {
|
|
3054
|
-
...lightDecls,
|
|
3055
|
-
...darkDecls
|
|
3056
|
-
};
|
|
3057
|
-
blocks.length = 0;
|
|
3058
|
-
blocks.push(formatBlock(BRANDING_SELECTOR, merged));
|
|
3059
|
-
}
|
|
3532
|
+
if (Object.keys(shared).length > 0) blocks.push(formatBlock(BRANDING_SELECTOR, shared));
|
|
3533
|
+
if (Object.keys(light).length > 0) blocks.push(formatBlock(LIGHT_SELECTOR, light));
|
|
3534
|
+
if (Object.keys(dark).length > 0) blocks.push(formatBlock(DARK_SELECTOR, dark));
|
|
3060
3535
|
return blocks.join("\n");
|
|
3061
3536
|
}
|
|
3062
3537
|
function collectDeclarations(branding) {
|
|
3063
3538
|
if (!branding) return {};
|
|
3064
3539
|
const decls = {};
|
|
3065
|
-
Object.assign(decls, mapPalette(branding.palette));
|
|
3066
3540
|
Object.assign(decls, mapTypography(branding.typography));
|
|
3067
3541
|
Object.assign(decls, mapShape(branding.shape));
|
|
3068
3542
|
return decls;
|
|
@@ -3083,24 +3557,52 @@ function mapTypography(typography) {
|
|
|
3083
3557
|
out["--zl-font-family-sans"] = typography.font_family;
|
|
3084
3558
|
out["--zl-font-family-heading"] = typography.font_family;
|
|
3085
3559
|
}
|
|
3086
|
-
if (typography.font_family_heading) out["--zl-font-family-heading"] = typography.font_family_heading;
|
|
3087
|
-
if (typography.font_family_mono) out["--zl-font-family-mono"] = typography.font_family_mono;
|
|
3088
3560
|
const scale = clamp(typography.scale ?? 1, .75, 1.25);
|
|
3089
|
-
if (scale !== 1)
|
|
3561
|
+
if (scale !== 1) for (const step of TEXT_STEPS) {
|
|
3562
|
+
out[`--zl-text-${step}-size`] = scaleRem(tokens.text[step].size, scale);
|
|
3563
|
+
out[`--zl-text-${step}-leading`] = scaleRem(tokens.text[step].leading, scale);
|
|
3564
|
+
}
|
|
3090
3565
|
return out;
|
|
3091
3566
|
}
|
|
3092
3567
|
function mapShape(shape) {
|
|
3093
3568
|
if (!shape) return {};
|
|
3094
3569
|
const out = {};
|
|
3095
|
-
|
|
3096
|
-
const radius = RADIUS_MAP[shape.radius];
|
|
3097
|
-
out["--zl-radius-md"] = radius;
|
|
3098
|
-
out["--zl-radius-lg"] = radius === "0" ? "0" : `calc(${radius} * 1.25)`;
|
|
3099
|
-
out["--zl-radius-xl"] = radius === "0" ? "0" : `calc(${radius} * 1.75)`;
|
|
3100
|
-
}
|
|
3570
|
+
Object.assign(out, mapRadius(shape.radius));
|
|
3101
3571
|
if (shape.density) Object.assign(out, DENSITY_MAP[shape.density]);
|
|
3572
|
+
const logoScale = shape.logo_scale;
|
|
3573
|
+
if (typeof logoScale === "number" && Number.isFinite(logoScale)) out["--zl-logo-scale"] = `${clamp(logoScale, .5, 2)}`;
|
|
3574
|
+
return out;
|
|
3575
|
+
}
|
|
3576
|
+
/**
|
|
3577
|
+
* A brand picks one corner value and the whole ramp follows it in proportion,
|
|
3578
|
+
* which is what keeps the card rounder than the controls inside it. Both forms
|
|
3579
|
+
* the revision accepts land here: a preset name, or an integer number of pixels
|
|
3580
|
+
* for the brand that has a specific value.
|
|
3581
|
+
*/
|
|
3582
|
+
function mapRadius(radius) {
|
|
3583
|
+
if (radius == null) return {};
|
|
3584
|
+
const out = {};
|
|
3585
|
+
if (radius === "full") {
|
|
3586
|
+
for (const step of RADIUS_STEPS) out[`--zl-radius-${step}`] = tokens.radius.full;
|
|
3587
|
+
return out;
|
|
3588
|
+
}
|
|
3589
|
+
const [base, unit] = typeof radius === "number" ? [radius, "px"] : radius in RADIUS_PRESETS ? [RADIUS_PRESETS[radius], "rem"] : [NaN, ""];
|
|
3590
|
+
if (Number.isNaN(base)) return {};
|
|
3591
|
+
for (const step of RADIUS_STEPS) {
|
|
3592
|
+
const value = base * RADIUS_RATIOS[step];
|
|
3593
|
+
out[`--zl-radius-${step}`] = value === 0 ? "0" : `${round(value)}${unit}`;
|
|
3594
|
+
}
|
|
3102
3595
|
return out;
|
|
3103
3596
|
}
|
|
3597
|
+
function scaleRem(value, scale) {
|
|
3598
|
+
return `${round(remToNumber(value) * scale)}rem`;
|
|
3599
|
+
}
|
|
3600
|
+
function remToNumber(value) {
|
|
3601
|
+
return Number.parseFloat(value);
|
|
3602
|
+
}
|
|
3603
|
+
function round(value) {
|
|
3604
|
+
return Math.round(value * 1e4) / 1e4;
|
|
3605
|
+
}
|
|
3104
3606
|
function formatBlock(selector, decls) {
|
|
3105
3607
|
return `${selector} {\n${Object.entries(decls).map(([prop, value]) => ` ${prop}: ${value};`).join("\n")}\n}`;
|
|
3106
3608
|
}
|
|
@@ -3140,8 +3642,8 @@ function applyBaseTokens(shadowRoot) {
|
|
|
3140
3642
|
* Callers should run `applyBaseTokens(shadowRoot)` first (once per shadow
|
|
3141
3643
|
* root) so the base values exist before branding patches them.
|
|
3142
3644
|
*/
|
|
3143
|
-
function applyBrandingTokens(shadowRoot, branding
|
|
3144
|
-
const css = buildBrandingStylesheet(branding
|
|
3645
|
+
function applyBrandingTokens(shadowRoot, branding) {
|
|
3646
|
+
const css = buildBrandingStylesheet(branding);
|
|
3145
3647
|
if (typeof CSSStyleSheet === "undefined") return;
|
|
3146
3648
|
const sheet = new CSSStyleSheet();
|
|
3147
3649
|
sheet.replaceSync(css);
|
|
@@ -3156,9 +3658,11 @@ function applyBrandingTokens(shadowRoot, branding, resolvedTheme) {
|
|
|
3156
3658
|
* and a variant-derived fallback on top of the same branding input.
|
|
3157
3659
|
*
|
|
3158
3660
|
* Defaults to dark: the design system's primary surface, and the mode a
|
|
3159
|
-
* hosted login page renders when a
|
|
3661
|
+
* hosted login page renders when a revision states no preference.
|
|
3160
3662
|
*/
|
|
3161
3663
|
function resolveTheme(branding) {
|
|
3664
|
+
const [only, second] = publishedSides(branding);
|
|
3665
|
+
if (only && !second) return only;
|
|
3162
3666
|
const mode = branding?.theme?.mode ?? "dark";
|
|
3163
3667
|
if (mode === "light") return "light";
|
|
3164
3668
|
if (mode === "dark") return "dark";
|
|
@@ -3176,7 +3680,7 @@ function resolveTheme(branding) {
|
|
|
3176
3680
|
* (Arimo, named first in `--zl-font-family-sans`). The component ships this
|
|
3177
3681
|
* so the auth screens render in the brand font out of the box, even when the
|
|
3178
3682
|
* server returns no branding. This is the "default users can override" layer.
|
|
3179
|
-
* - The **tenant override** (`applyFontUrl`) — `branding.font_url`, resolved
|
|
3683
|
+
* - The **tenant override** (`applyFontUrl`) — `branding.typography.font_url`, resolved
|
|
3180
3684
|
* server-side from the app → team → project branding hierarchy.
|
|
3181
3685
|
*
|
|
3182
3686
|
* Both must live at document level, not inside the shadow root: browsers ignore
|
|
@@ -3205,7 +3709,7 @@ const DEFAULT_LINK_ID = "zl-default-font-link";
|
|
|
3205
3709
|
* Design-system default brand font. Arimo is the Figma body face and leads the
|
|
3206
3710
|
* `--zl-font-family-sans` stack; loading it here makes the unbranded auth UI
|
|
3207
3711
|
* paint in the brand font instead of the system fallback. Tenants override it
|
|
3208
|
-
* with `branding.font_url`. Exported so a privacy- or offline-sensitive
|
|
3712
|
+
* with `branding.typography.font_url`. Exported so a privacy- or offline-sensitive
|
|
3209
3713
|
* deployment can self-host the same face and swap this single URL.
|
|
3210
3714
|
*/
|
|
3211
3715
|
const DEFAULT_BRAND_FONT_HREF = "https://fonts.googleapis.com/css2?family=Arimo:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap";
|
|
@@ -3225,7 +3729,7 @@ const DEFAULT_BRAND_FONT_HREF = "https://fonts.googleapis.com/css2?family=Arimo:
|
|
|
3225
3729
|
/** Per-document, per-link-id map of each owner's desired href. */
|
|
3226
3730
|
const registries = /* @__PURE__ */ new WeakMap();
|
|
3227
3731
|
/**
|
|
3228
|
-
* Inject the tenant's `branding.font_url` stylesheet. Pass `null`/`undefined`
|
|
3732
|
+
* Inject the tenant's `branding.typography.font_url` stylesheet. Pass `null`/`undefined`
|
|
3229
3733
|
* to withdraw this surface's registration (e.g. when a flow resolves with no
|
|
3230
3734
|
* tenant font); the link disappears once no surface registers a URL.
|
|
3231
3735
|
*/
|
|
@@ -3235,7 +3739,7 @@ function applyFontUrl(shadowRoot, fontUrl) {
|
|
|
3235
3739
|
/**
|
|
3236
3740
|
* Inject the design-system default font stylesheet. Defaults to
|
|
3237
3741
|
* {@link DEFAULT_BRAND_FONT_HREF}; pass `null` to withdraw this surface's
|
|
3238
|
-
* registration (the orchestrator does this when a tenant
|
|
3742
|
+
* registration (the orchestrator does this when a tenant font stylesheet takes
|
|
3239
3743
|
* over, to avoid a redundant request).
|
|
3240
3744
|
*/
|
|
3241
3745
|
function applyDefaultFont(shadowRoot, href = DEFAULT_BRAND_FONT_HREF) {
|
|
@@ -3302,7 +3806,10 @@ var ThemeController = class {
|
|
|
3302
3806
|
* element nor the branding payload names a mode. Precedence, strongest
|
|
3303
3807
|
* first: element `theme` property → `branding.theme.mode` → fallback. The
|
|
3304
3808
|
* element wins because the page embedding the widget knows its own surface
|
|
3305
|
-
* better than the tenant's stored branding does
|
|
3809
|
+
* better than the tenant's stored branding does — but it selects among the
|
|
3810
|
+
* sides the revision published, and cannot reach one it did not. A revision
|
|
3811
|
+
* with a single published side therefore ignores this property: an embedder
|
|
3812
|
+
* pinning `theme="light"` against a dark-only revision gets dark.
|
|
3306
3813
|
*/
|
|
3307
3814
|
setModePreference(explicit, fallback) {
|
|
3308
3815
|
if (this.explicitMode === explicit && this.fallbackMode === fallback) return;
|
|
@@ -3317,6 +3824,12 @@ var ThemeController = class {
|
|
|
3317
3824
|
this.detach();
|
|
3318
3825
|
}
|
|
3319
3826
|
refresh() {
|
|
3827
|
+
const [only, second] = publishedSides(this.branding);
|
|
3828
|
+
if (only && !second) {
|
|
3829
|
+
this.detach();
|
|
3830
|
+
this.update(only);
|
|
3831
|
+
return;
|
|
3832
|
+
}
|
|
3320
3833
|
const mode = this.explicitMode ?? this.branding?.theme?.mode ?? this.fallbackMode;
|
|
3321
3834
|
if (mode === "auto") {
|
|
3322
3835
|
this.attach();
|
|
@@ -3395,9 +3908,10 @@ var ZitadelSurface = class extends LitElement {
|
|
|
3395
3908
|
const root = this.shadowRoot;
|
|
3396
3909
|
if (root) {
|
|
3397
3910
|
applyBaseTokens(root);
|
|
3398
|
-
applyBrandingTokens(root, branding
|
|
3399
|
-
const
|
|
3400
|
-
|
|
3911
|
+
applyBrandingTokens(root, branding);
|
|
3912
|
+
const page = this.variant === "page";
|
|
3913
|
+
const tenantFontUrl = page ? branding?.typography?.font_url ?? null : null;
|
|
3914
|
+
applyDefaultFont(root, page && !tenantFontUrl ? void 0 : null);
|
|
3401
3915
|
applyFontUrl(root, tenantFontUrl);
|
|
3402
3916
|
}
|
|
3403
3917
|
this.dataset.theme = this.themeController.theme;
|
|
@@ -3433,7 +3947,7 @@ const TEMPLATE_NAMES = {
|
|
|
3433
3947
|
};
|
|
3434
3948
|
//#endregion
|
|
3435
3949
|
//#region src/orchestrator/templates/layout-chrome.css?inline
|
|
3436
|
-
var layout_chrome_default = ":host {\n color: var(--zl-foreground);\n font-family: var(--zl-font-family-sans);\n background: none;\n display: block;\n}\n\n:host([variant=\"page\"]) {\n background: var(--zl-background);\n min-height: 100%;\n}\n\nimg[data-zl-asset-broken] {\n display: none;\n}\n\n.zl-mount {\n min-height: var(--zl-page-min-height, auto);\n background: inherit;\n color: inherit;\n flex-direction: column;\n display: flex;\n container-type: inline-size;\n}\n\n:host([variant=\"page\"]) .zl-mount {\n min-height: var(--zl-page-min-height, 100vh);\n}\n\n.zl-mount > zl-page-shell, .zl-mount > [data-zl-template-root] {\n flex: auto;\n}\n\n.zl-attribution {\n background: none;\n justify-content: center;\n align-items: center;\n display: flex;\n}\n\n.zl-attribution[hidden] {\n display: none;\n}\n\n.zl-trustmark {\n gap: var(--zl-spacing-2-5);\n flex-wrap: wrap;\n}\n\n.zl-trustmark__mark {\n align-items: center;\n gap: var(--zl-spacing-2);\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n color: var(--zl-foreground);\n text-decoration: none;\n display: inline-flex;\n}\n\n.zl-trustmark__mark:hover {\n text-underline-offset: 4px;\n text-decoration: underline;\n}\n\n.zl-trustmark__mark:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n border-radius: var(--zl-radius-sm);\n}\n\n.zl-trustmark__logotype {\n flex-shrink: 0;\n width: 65px;\n height: 16px;\n line-height: 0;\n display: block;\n}\n\n.zl-trustmark__logotype-svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n.zl-card-logo {\n width: auto;\n max-width: min(12rem, 60%);\n max-height: var(--zl-card-logo-max-height, 2.5rem);\n object-fit: contain;\n}\n\n.zl-card-title {\n font-family: var(--zl-font-family-heading);\n font-size: var(--zl-text-xl-size);\n font-weight: var(--zl-font-weight-medium);\n color: var(--zl-card-foreground);\n text-align: left;\n margin: 0;\n line-height: 1;\n}\n\n.zl-card-subtitle {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-muted-foreground);\n margin: 0;\n}\n\n.zl-field-group {\n gap: var(--zl-spacing-7);\n flex-direction: column;\n display: flex;\n}\n\n.zl-field-group:not(:has(*)) {\n display: none;\n}\n\n.zl-card-actions {\n gap: var(--zl-spacing-3);\n flex-direction: column;\n display: flex;\n}\n\n[data-suppress-header] .zl-card-title, [data-suppress-header] .zl-card-subtitle {\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n position: absolute;\n overflow: hidden;\n}\n\n.zl-card-nav {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-muted-foreground);\n text-align: center;\n margin: 0;\n}\n\n.zl-card-nav__link {\n font: inherit;\n line-height: inherit;\n color: var(--zl-link);\n text-underline-offset: 4px;\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n text-decoration: underline;\n display: inline;\n}\n\n.zl-card-nav__link:hover {\n color: var(--zl-foreground);\n}\n\n.zl-card-nav__link:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n}\n\n.zl-card-forgot {\n text-align: right;\n margin: 0;\n}\n\n.zl-card-forgot__link {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-link);\n text-underline-offset: 4px;\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n text-decoration: none;\n display: inline;\n}\n\n.zl-card-forgot__link:hover {\n text-decoration: underline;\n}\n\n.zl-card-forgot__link:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n}\n\n.zl-split {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n align-items: var(--zl-split-align, center);\n gap: var(--zl-spacing-8);\n width: 100%;\n min-height: 100%;\n display: grid;\n}\n\n.zl-split__brand {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-6);\n padding: var(--zl-spacing-8) var(--zl-spacing-4);\n flex-direction: column;\n display: flex;\n}\n\n.zl-split__logo {\n max-width: min(16rem, 60%);\n max-height: var(--zl-split-logo-max-height, 16rem);\n}\n\n.zl-split__logo:has( + .zl-split__hero) {\n max-height: var(--zl-split-logo-max-height, 8rem);\n}\n\n.zl-split__logo:has( + .zl-split__hero) + .zl-split__hero {\n max-height: min(var(--zl-split-hero-max-height, 22.5rem),\n max(6rem,\n calc(100vh - 104px - var(--zl-spacing-8) - var(--zl-spacing-8) -\n 2.5rem - var(--zl-spacing-6) - var(--zl-spacing-6) -\n var(--zl-split-logo-max-height, 8rem))));\n}\n\n.zl-split__hero {\n width: 100%;\n max-height: var(--zl-split-hero-max-height, min(22.5rem, 60vh));\n border-radius: var(--zl-radius-lg);\n object-fit: cover;\n}\n\n.zl-split__placeholder {\n aspect-ratio: 4 / 5;\n border-radius: var(--zl-radius-lg);\n background: radial-gradient(120% 120% at 18% 12%,\n var(--zl-gradient-lavender-start) 0%,\n transparent 60%),\n radial-gradient(130% 130% at 85% 92%,\n var(--zl-gradient-rose-start) 0%,\n transparent 60%),\n var(--zl-card);\n width: min(18rem, 85%);\n}\n\n.zl-split__form {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-6);\n flex-direction: column;\n padding: 16px;\n display: flex;\n}\n\n.zl-split__compact {\n max-width: min(12rem, 60%);\n display: none;\n}\n\nimg.zl-split__compact {\n max-height: 2.5rem;\n}\n\nimg.zl-split__compact--hero {\n border-radius: var(--zl-radius-lg);\n object-fit: cover;\n width: 100%;\n max-width: 100%;\n max-height: 6rem;\n}\n\n.zl-split--right .zl-split__brand {\n order: 2;\n}\n\n.zl-split--right .zl-split__form {\n order: 1;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n gap: var(--zl-spacing-8);\n width: 100%;\n display: grid;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution > * {\n grid-column: 2;\n justify-self: center;\n}\n\nzl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution > * {\n grid-column: 1;\n}\n\n@container (width <= 48rem) {\n .zl-split {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .zl-split__brand {\n display: var(--zl-split-brand-mobile, none);\n }\n\n .zl-split__compact {\n display: block;\n }\n\n zl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution {\n grid-template-columns: minmax(0, 1fr);\n }\n\n zl-page-shell:has(.zl-split) .zl-attribution > *, zl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution > * {\n grid-column: 1;\n }\n}\n\n.zl-hero {\n text-align: left;\n width: 100%;\n max-width: 34rem;\n color: var(--zl-foreground);\n font-family: var(--zl-font-family-sans);\n flex-direction: column;\n display: flex;\n}\n\n.zl-hero__nav {\n margin-bottom: var(--zl-spacing-8);\n justify-content: space-between;\n align-items: center;\n display: flex;\n}\n\n.zl-hero__brand {\n align-items: center;\n gap: var(--zl-spacing-2);\n letter-spacing: -.01em;\n font-size: 1.125rem;\n font-weight: 700;\n display: inline-flex;\n}\n\n.zl-hero__brand-logo {\n max-height: 1.75rem;\n}\n\n.zl-hero__brand-mark {\n border-radius: var(--zl-radius-md);\n background: linear-gradient(135deg,\n var(--zl-gradient-lavender-start),\n var(--zl-gradient-rose-start));\n width: 1.75rem;\n height: 1.75rem;\n display: inline-flex;\n}\n\n.zl-hero__compact-brand {\n overflow-wrap: anywhere;\n letter-spacing: -.01em;\n max-width: 100%;\n color: var(--zl-foreground);\n margin: 0;\n font-size: 1.125rem;\n font-weight: 700;\n}\n\n.zl-hero__nav-links {\n gap: var(--zl-spacing-6);\n color: var(--zl-muted-foreground);\n font-size: .9375rem;\n display: flex;\n}\n\n.zl-hero__nav-links a {\n color: inherit;\n text-decoration: none;\n}\n\n.zl-hero__nav-links a:hover {\n text-decoration: underline;\n}\n\n.zl-hero__eyebrow {\n margin: 0 0 var(--zl-spacing-4);\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--zl-muted-foreground);\n font-size: .8125rem;\n font-weight: 600;\n}\n\n.zl-hero__headline {\n margin: 0 0 var(--zl-spacing-4);\n font-family: var(--zl-font-family-heading);\n letter-spacing: -.02em;\n font-size: 2.75rem;\n font-weight: 700;\n line-height: 1.1;\n}\n\n.zl-hero__sub {\n margin: 0 0 var(--zl-spacing-8);\n max-width: 30rem;\n color: var(--zl-muted-foreground);\n font-size: 1.0625rem;\n line-height: 1.6;\n}\n\n.zl-hero__bullets {\n gap: var(--zl-spacing-2);\n margin: 0 0 var(--zl-spacing-8);\n flex-direction: column;\n padding: 0;\n list-style: none;\n display: flex;\n}\n\n.zl-hero__bullets li {\n align-items: center;\n gap: var(--zl-spacing-2);\n font-size: 1rem;\n display: flex;\n}\n\n.zl-hero__bullets li:before {\n content: \"✓\";\n color: var(--zl-success);\n font-weight: 700;\n}\n\n.zl-hero__footnote {\n padding-top: var(--zl-spacing-6);\n border-top: 1px solid var(--zl-border);\n color: var(--zl-muted-foreground);\n margin: 0;\n font-size: .8125rem;\n}\n\n.zl-hero__footnote strong {\n color: var(--zl-muted-foreground);\n font-weight: 600;\n}\n\n.zl-minimal {\n gap: var(--zl-spacing-6);\n flex-direction: column;\n width: min(24rem, 100%);\n margin-inline: auto;\n display: flex;\n}\n\n:host([variant=\"page\"]) .zl-minimal {\n padding: 52px 16px;\n}\n";
|
|
3950
|
+
var layout_chrome_default = ":host {\n color: var(--zl-foreground);\n font-family: var(--zl-font-family-sans);\n background: none;\n display: block;\n}\n\n:host([variant=\"page\"]) {\n background: var(--zl-background);\n min-height: 100%;\n}\n\nimg[data-zl-asset-broken] {\n display: none;\n}\n\n.zl-mount {\n min-height: var(--zl-page-min-height, auto);\n background: inherit;\n color: inherit;\n flex-direction: column;\n display: flex;\n container-type: inline-size;\n}\n\n:host([variant=\"page\"]) .zl-mount {\n min-height: var(--zl-page-min-height, 100vh);\n}\n\n.zl-mount > zl-page-shell, .zl-mount > [data-zl-template-root] {\n flex: auto;\n}\n\n.zl-attribution {\n background: none;\n justify-content: center;\n align-items: center;\n display: flex;\n}\n\n.zl-attribution[hidden] {\n display: none;\n}\n\n.zl-trustmark {\n gap: var(--zl-spacing-2-5);\n flex-wrap: wrap;\n}\n\n.zl-trustmark__mark {\n align-items: center;\n gap: var(--zl-spacing-2);\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n color: var(--zl-foreground);\n text-decoration: none;\n display: inline-flex;\n}\n\n.zl-trustmark__mark:hover {\n text-underline-offset: 4px;\n text-decoration: underline;\n}\n\n.zl-trustmark__mark:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n border-radius: var(--zl-radius-sm);\n}\n\n.zl-trustmark__logotype {\n flex-shrink: 0;\n width: 65px;\n height: 16px;\n line-height: 0;\n display: block;\n}\n\n.zl-trustmark__logotype-svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n.zl-card-logo {\n width: auto;\n max-width: min(12rem, 60%);\n max-height: calc(var(--zl-card-logo-max-height, 2.5rem) * var(--zl-logo-scale, 1));\n object-fit: contain;\n}\n\n.zl-card-title {\n font-family: var(--zl-font-family-heading);\n font-size: var(--zl-text-xl-size);\n font-weight: var(--zl-font-weight-medium);\n color: var(--zl-card-foreground);\n text-align: left;\n margin: 0;\n line-height: 1;\n}\n\n.zl-card-subtitle {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-muted-foreground);\n margin: 0;\n}\n\n.zl-field-group {\n gap: var(--zl-spacing-7);\n flex-direction: column;\n display: flex;\n}\n\n.zl-field-group:not(:has(*)) {\n display: none;\n}\n\n.zl-card-actions {\n gap: var(--zl-spacing-3);\n flex-direction: column;\n display: flex;\n}\n\n[data-suppress-header] .zl-card-title, [data-suppress-header] .zl-card-subtitle {\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n position: absolute;\n overflow: hidden;\n}\n\n.zl-card-nav {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-muted-foreground);\n text-align: center;\n margin: 0;\n}\n\n.zl-card-nav__link {\n font: inherit;\n line-height: inherit;\n color: var(--zl-link);\n text-underline-offset: 4px;\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n text-decoration: underline;\n display: inline;\n}\n\n.zl-card-nav__link:hover {\n color: var(--zl-foreground);\n}\n\n.zl-card-nav__link:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n}\n\n.zl-card-forgot {\n text-align: right;\n margin: 0;\n}\n\n.zl-card-forgot__link {\n font-family: var(--zl-font-family-sans);\n font-size: var(--zl-text-sm-size);\n line-height: var(--zl-text-sm-leading);\n font-weight: var(--zl-font-weight-normal);\n color: var(--zl-link);\n text-underline-offset: 4px;\n cursor: pointer;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n text-decoration: none;\n display: inline;\n}\n\n.zl-card-forgot__link:hover {\n text-decoration: underline;\n}\n\n.zl-card-forgot__link:focus-visible {\n outline: var(--zl-focus-width) solid var(--zl-ring);\n outline-offset: var(--zl-focus-offset);\n}\n\n.zl-split {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n align-items: var(--zl-split-align, center);\n gap: var(--zl-spacing-8);\n width: 100%;\n min-height: 100%;\n display: grid;\n}\n\n.zl-split__brand {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-6);\n padding: var(--zl-spacing-8) var(--zl-spacing-4);\n flex-direction: column;\n display: flex;\n}\n\n.zl-split__logo {\n max-width: min(16rem, 60%);\n max-height: calc(var(--zl-split-logo-max-height, 16rem) * var(--zl-logo-scale, 1));\n}\n\n.zl-split__logo:has( + .zl-split__hero) {\n max-height: calc(var(--zl-split-logo-max-height, 8rem) * var(--zl-logo-scale, 1));\n}\n\n.zl-split__logo:has( + .zl-split__hero) + .zl-split__hero {\n max-height: min(var(--zl-split-hero-max-height, 22.5rem),\n max(6rem,\n calc(100vh - 104px - var(--zl-spacing-8) - var(--zl-spacing-8) -\n 2.5rem - var(--zl-spacing-6) - var(--zl-spacing-6) -\n calc(var(--zl-split-logo-max-height, 8rem) * var(--zl-logo-scale, 1)))));\n}\n\n.zl-split__hero {\n width: 100%;\n max-height: var(--zl-split-hero-max-height, min(22.5rem, 60vh));\n border-radius: var(--zl-radius-lg);\n object-fit: cover;\n}\n\n.zl-split__placeholder {\n aspect-ratio: 4 / 5;\n border-radius: var(--zl-radius-lg);\n background: radial-gradient(120% 120% at 18% 12%,\n var(--zl-gradient-lavender-start) 0%,\n transparent 60%),\n radial-gradient(130% 130% at 85% 92%,\n var(--zl-gradient-rose-start) 0%,\n transparent 60%),\n var(--zl-card);\n width: min(18rem, 85%);\n}\n\n.zl-split__form {\n justify-content: center;\n align-items: center;\n gap: var(--zl-spacing-6);\n flex-direction: column;\n padding: 16px;\n display: flex;\n}\n\n.zl-split__compact {\n max-width: min(12rem, 60%);\n display: none;\n}\n\nimg.zl-split__compact {\n max-height: calc(2.5rem * var(--zl-logo-scale, 1));\n}\n\nimg.zl-split__compact--hero {\n border-radius: var(--zl-radius-lg);\n object-fit: cover;\n width: 100%;\n max-width: 100%;\n max-height: 6rem;\n}\n\n.zl-split--right .zl-split__brand {\n order: 2;\n}\n\n.zl-split--right .zl-split__form {\n order: 1;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution {\n grid-template-columns: var(--zl-split-columns, minmax(0, 1fr) minmax(0, 1fr));\n gap: var(--zl-spacing-8);\n width: 100%;\n display: grid;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution > * {\n grid-column: 2;\n justify-self: center;\n}\n\nzl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution > * {\n grid-column: 1;\n}\n\nzl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution ::slotted(*) {\n grid-column: 2;\n justify-self: center;\n}\n\nzl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution ::slotted(*) {\n grid-column: 1;\n}\n\n@container (width <= 48rem) {\n .zl-split {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .zl-split__brand {\n display: var(--zl-split-brand-mobile, none);\n }\n\n .zl-split__compact {\n display: block;\n }\n\n zl-page-shell:has(.zl-split) [slot=\"footer\"].zl-attribution {\n grid-template-columns: minmax(0, 1fr);\n }\n\n zl-page-shell:has(.zl-split) .zl-attribution > *, zl-page-shell:has(.zl-split--right) [slot=\"footer\"].zl-attribution > * {\n grid-column: 1;\n }\n}\n\n.zl-hero {\n text-align: left;\n width: 100%;\n max-width: 34rem;\n color: var(--zl-foreground);\n font-family: var(--zl-font-family-sans);\n flex-direction: column;\n display: flex;\n}\n\n.zl-hero__nav {\n margin-bottom: var(--zl-spacing-8);\n justify-content: space-between;\n align-items: center;\n display: flex;\n}\n\n.zl-hero__brand {\n align-items: center;\n gap: var(--zl-spacing-2);\n letter-spacing: -.01em;\n font-size: 1.125rem;\n font-weight: 700;\n display: inline-flex;\n}\n\n.zl-hero__brand-logo {\n max-height: calc(1.75rem * var(--zl-logo-scale, 1));\n}\n\n.zl-hero__brand-mark {\n border-radius: var(--zl-radius-md);\n background: linear-gradient(135deg,\n var(--zl-gradient-lavender-start),\n var(--zl-gradient-rose-start));\n width: 1.75rem;\n height: 1.75rem;\n display: inline-flex;\n}\n\n.zl-hero__compact-brand {\n overflow-wrap: anywhere;\n letter-spacing: -.01em;\n max-width: 100%;\n color: var(--zl-foreground);\n margin: 0;\n font-size: 1.125rem;\n font-weight: 700;\n}\n\n.zl-hero__nav-links {\n gap: var(--zl-spacing-6);\n color: var(--zl-muted-foreground);\n font-size: .9375rem;\n display: flex;\n}\n\n.zl-hero__nav-links a {\n color: inherit;\n text-decoration: none;\n}\n\n.zl-hero__nav-links a:hover {\n text-decoration: underline;\n}\n\n.zl-hero__eyebrow {\n margin: 0 0 var(--zl-spacing-4);\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--zl-muted-foreground);\n font-size: .8125rem;\n font-weight: 600;\n}\n\n.zl-hero__headline {\n margin: 0 0 var(--zl-spacing-4);\n font-family: var(--zl-font-family-heading);\n letter-spacing: -.02em;\n font-size: 2.75rem;\n font-weight: 700;\n line-height: 1.1;\n}\n\n.zl-hero__sub {\n margin: 0 0 var(--zl-spacing-8);\n max-width: 30rem;\n color: var(--zl-muted-foreground);\n font-size: 1.0625rem;\n line-height: 1.6;\n}\n\n.zl-hero__bullets {\n gap: var(--zl-spacing-2);\n margin: 0 0 var(--zl-spacing-8);\n flex-direction: column;\n padding: 0;\n list-style: none;\n display: flex;\n}\n\n.zl-hero__bullets li {\n align-items: center;\n gap: var(--zl-spacing-2);\n font-size: 1rem;\n display: flex;\n}\n\n.zl-hero__bullets li:before {\n content: \"✓\";\n color: var(--zl-success);\n font-weight: 700;\n}\n\n.zl-hero__footnote {\n padding-top: var(--zl-spacing-6);\n border-top: 1px solid var(--zl-border);\n color: var(--zl-muted-foreground);\n margin: 0;\n font-size: .8125rem;\n}\n\n.zl-hero__footnote strong {\n color: var(--zl-muted-foreground);\n font-weight: 600;\n}\n\n.zl-minimal {\n gap: var(--zl-spacing-6);\n flex-direction: column;\n width: min(24rem, 100%);\n margin-inline: auto;\n display: flex;\n}\n\n:host([variant=\"page\"]) .zl-minimal {\n padding: 52px 16px;\n}\n";
|
|
3437
3951
|
//#endregion
|
|
3438
3952
|
//#region src/orchestrator/zitadel-login.ts
|
|
3439
3953
|
/** Narrow a rendered named element to the `formValue` field-atom contract. */
|
|
@@ -3542,6 +4056,13 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3542
4056
|
set loading(value) {
|
|
3543
4057
|
this.#_loading_accessor_storage = value;
|
|
3544
4058
|
}
|
|
4059
|
+
#_completing_accessor_storage = false;
|
|
4060
|
+
get completing() {
|
|
4061
|
+
return this.#_completing_accessor_storage;
|
|
4062
|
+
}
|
|
4063
|
+
set completing(value) {
|
|
4064
|
+
this.#_completing_accessor_storage = value;
|
|
4065
|
+
}
|
|
3545
4066
|
#_startupError_accessor_storage = null;
|
|
3546
4067
|
get startupError() {
|
|
3547
4068
|
return this.#_startupError_accessor_storage;
|
|
@@ -3557,6 +4078,13 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3557
4078
|
this.#_formValues_accessor_storage = value;
|
|
3558
4079
|
}
|
|
3559
4080
|
engine = null;
|
|
4081
|
+
/**
|
|
4082
|
+
* The theme the last commit rendered with. The template output carries the
|
|
4083
|
+
* resolved side's mark, so a theme change rewrites the string and
|
|
4084
|
+
* `unsafeHTML` rebuilds the form — which drops what the visitor had typed
|
|
4085
|
+
* unless it is put back.
|
|
4086
|
+
*/
|
|
4087
|
+
lastRenderedTheme = null;
|
|
3560
4088
|
sanitise = createSanitiser();
|
|
3561
4089
|
/**
|
|
3562
4090
|
* Cached compiled tenant template, keyed by source string. Re-rendering on
|
|
@@ -3655,9 +4183,14 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3655
4183
|
}
|
|
3656
4184
|
for (const card of this.shadowRoot.querySelectorAll("zl-card")) card.toggleAttribute("data-suppress-header", this.suppressHeader);
|
|
3657
4185
|
}
|
|
4186
|
+
const previousTheme = this.lastRenderedTheme;
|
|
4187
|
+
this.lastRenderedTheme = this.themeController.theme;
|
|
3658
4188
|
const props = changed;
|
|
3659
|
-
if (
|
|
3660
|
-
|
|
4189
|
+
if (props.has("response")) {
|
|
4190
|
+
this.hydrateStepAfterRender(props.get("response") == null);
|
|
4191
|
+
return;
|
|
4192
|
+
}
|
|
4193
|
+
if (previousTheme !== null && previousTheme !== this.lastRenderedTheme) this.restoreValuesAfterRender();
|
|
3661
4194
|
}
|
|
3662
4195
|
/**
|
|
3663
4196
|
* Apply captured values and move focus once the new step has fully
|
|
@@ -3672,11 +4205,19 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3672
4205
|
* swaps are user-initiated, so focus moves in both modes.
|
|
3673
4206
|
*/
|
|
3674
4207
|
async hydrateStepAfterRender(initial = false) {
|
|
4208
|
+
await this.restoreValuesAfterRender();
|
|
4209
|
+
if (!initial || this.variant === "page") this.moveFocusToFirstField(initial && this.variant === "page");
|
|
4210
|
+
}
|
|
4211
|
+
/**
|
|
4212
|
+
* Put captured values back once the rebuilt subtree has rendered. Awaits the
|
|
4213
|
+
* child atoms' own first render before touching them, rather than guessing a
|
|
4214
|
+
* frame with `requestAnimationFrame`.
|
|
4215
|
+
*/
|
|
4216
|
+
async restoreValuesAfterRender() {
|
|
3675
4217
|
await this.updateComplete;
|
|
3676
4218
|
const atoms = this.shadowRoot?.querySelectorAll("zl-field, zl-select, zl-checkbox, zl-button");
|
|
3677
4219
|
if (atoms) await Promise.all(Array.from(atoms).map((atom) => atom.updateComplete));
|
|
3678
4220
|
this.applyValuesToFields();
|
|
3679
|
-
if (!initial || this.variant === "page") this.moveFocusToFirstField(initial && this.variant === "page");
|
|
3680
4221
|
}
|
|
3681
4222
|
render() {
|
|
3682
4223
|
if (this.startupError) return html`<form class="zl-mount" novalidate>
|
|
@@ -3686,7 +4227,7 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3686
4227
|
</zl-card>
|
|
3687
4228
|
</zl-page-shell>
|
|
3688
4229
|
</form>`;
|
|
3689
|
-
if (!this.response || !this.engine) return html`<slot name="loader"></slot>`;
|
|
4230
|
+
if (!this.response || !this.engine || this.completing) return html`<slot name="loader"></slot>`;
|
|
3690
4231
|
const rendered = this.injectAttribution(this.renderStep(this.response.step, this.engine));
|
|
3691
4232
|
return html`<form
|
|
3692
4233
|
class="zl-mount"
|
|
@@ -3718,6 +4259,20 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3718
4259
|
return rendered + html;
|
|
3719
4260
|
}
|
|
3720
4261
|
/**
|
|
4262
|
+
* The branding a template renders against, with `logo_url` already resolved
|
|
4263
|
+
* to the mark for the active side. Templates read one logo field and get the
|
|
4264
|
+
* right file per surface; the per-side URLs stay on `theme` for a template
|
|
4265
|
+
* that wants to reach them itself.
|
|
4266
|
+
*/
|
|
4267
|
+
brandingForTemplate() {
|
|
4268
|
+
if (!this.branding) return {};
|
|
4269
|
+
const logoUrl = resolveLogoUrl(this.branding, this.themeController.theme);
|
|
4270
|
+
return {
|
|
4271
|
+
...this.branding,
|
|
4272
|
+
logo_url: logoUrl
|
|
4273
|
+
};
|
|
4274
|
+
}
|
|
4275
|
+
/**
|
|
3721
4276
|
* "Secured with Zitadel" attribution chrome injected into every
|
|
3722
4277
|
* template's page-shell footer slot. Controlled by
|
|
3723
4278
|
* `branding.attribution.show_zitadel` — defaults to `true` for
|
|
@@ -3730,7 +4285,7 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3730
4285
|
const custom = attribution?.custom_link;
|
|
3731
4286
|
if (!show && !custom) return "";
|
|
3732
4287
|
const mark = custom ? `<a class="zl-trustmark__mark" part="attribution-mark" href="${escapeHtml(String(custom.href))}">${escapeHtml(String(custom.label))}</a>` : `<a class="zl-trustmark__mark" part="attribution-mark" href="https://zitadel.com" aria-label="Secured with Zitadel">${zitadelTrustmarkInnerHtml()}</a>`;
|
|
3733
|
-
return `<div${placement === "footer" ? ` slot="footer"` : ""} part="attribution" class="zl-attribution zl-trustmark">${mark}
|
|
4288
|
+
return `<div${placement === "footer" ? ` slot="footer"` : ""} part="attribution" class="zl-attribution zl-trustmark">${mark}<slot name="attribution-trailing"></slot></div>`;
|
|
3734
4289
|
}
|
|
3735
4290
|
/** Declarative config read from this element's attributes. */
|
|
3736
4291
|
get projectAttrs() {
|
|
@@ -3778,6 +4333,7 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3778
4333
|
}
|
|
3779
4334
|
applyResponse(wire) {
|
|
3780
4335
|
this.stepErrorDismissed = false;
|
|
4336
|
+
this.completing = navigatesOnComplete(wire, this.postSignInUrl);
|
|
3781
4337
|
this.response = wire;
|
|
3782
4338
|
const { branding, issues } = validateBranding(wire.branding, { renderingOrigin: this.ownerDocument.location.origin });
|
|
3783
4339
|
this.branding = branding;
|
|
@@ -3797,7 +4353,11 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3797
4353
|
this.armed = true;
|
|
3798
4354
|
} else if (!hasBack && this.armed) {
|
|
3799
4355
|
this.armed = false;
|
|
3800
|
-
if (history.state?.zl === true) {
|
|
4356
|
+
if (history.state?.zl === true) if (this.completing) history.replaceState({
|
|
4357
|
+
...history.state,
|
|
4358
|
+
zl: false
|
|
4359
|
+
}, "");
|
|
4360
|
+
else {
|
|
3801
4361
|
this.ignoreNextPop = true;
|
|
3802
4362
|
history.back();
|
|
3803
4363
|
}
|
|
@@ -3869,7 +4429,7 @@ let ZitadelLogin = class ZitadelLogin extends ZitadelSurface {
|
|
|
3869
4429
|
messages: [],
|
|
3870
4430
|
identity: this.deriveIdentity(),
|
|
3871
4431
|
errors,
|
|
3872
|
-
branding: this.
|
|
4432
|
+
branding: this.brandingForTemplate(),
|
|
3873
4433
|
loading: this.loading
|
|
3874
4434
|
};
|
|
3875
4435
|
let raw;
|
|
@@ -4249,6 +4809,7 @@ __decorate([property({ attribute: false })], ZitadelLogin.prototype, "locales",
|
|
|
4249
4809
|
__decorate([state()], ZitadelLogin.prototype, "response", null);
|
|
4250
4810
|
__decorate([state()], ZitadelLogin.prototype, "branding", null);
|
|
4251
4811
|
__decorate([state()], ZitadelLogin.prototype, "loading", null);
|
|
4812
|
+
__decorate([state()], ZitadelLogin.prototype, "completing", null);
|
|
4252
4813
|
__decorate([state()], ZitadelLogin.prototype, "startupError", null);
|
|
4253
4814
|
__decorate([state()], ZitadelLogin.prototype, "formValues", null);
|
|
4254
4815
|
ZitadelLogin = __decorate([customElement("zitadel-login")], ZitadelLogin);
|
|
@@ -4262,6 +4823,19 @@ ZitadelLogin = __decorate([customElement("zitadel-login")], ZitadelLogin);
|
|
|
4262
4823
|
function isAllowedSelectValue(field, value) {
|
|
4263
4824
|
return field.validation?.enum?.includes(value) ?? false;
|
|
4264
4825
|
}
|
|
4826
|
+
/**
|
|
4827
|
+
* Whether a terminal step ends in a navigation rather than a screen: a
|
|
4828
|
+
* `redirect` with a URI, or a `show` whose handoff the widget exchanges before
|
|
4829
|
+
* sending the browser to `post-sign-in-url`. A `show` without a
|
|
4830
|
+
* `post-sign-in-url` is the host handling the handoff itself, and that screen
|
|
4831
|
+
* is the flow's own last word — it still renders.
|
|
4832
|
+
*/
|
|
4833
|
+
function navigatesOnComplete(wire, postSignInUrl) {
|
|
4834
|
+
const behavior = wire.step.complete;
|
|
4835
|
+
if (behavior === "redirect") return Boolean(wire.redirect_uri);
|
|
4836
|
+
if (behavior === "show") return Boolean(wire.handoff_token && postSignInUrl);
|
|
4837
|
+
return false;
|
|
4838
|
+
}
|
|
4265
4839
|
function collectInitialValues(step) {
|
|
4266
4840
|
const values = {};
|
|
4267
4841
|
if (!step.fields) return values;
|
|
@@ -4519,7 +5093,7 @@ let ZitadelLogout = class ZitadelLogout extends LitElement {
|
|
|
4519
5093
|
const root = this.shadowRoot;
|
|
4520
5094
|
if (root && !this.templateMode) {
|
|
4521
5095
|
applyBaseTokens(root);
|
|
4522
|
-
applyBrandingTokens(root, void 0
|
|
5096
|
+
applyBrandingTokens(root, void 0);
|
|
4523
5097
|
}
|
|
4524
5098
|
this.dataset.theme = this.themeController.theme;
|
|
4525
5099
|
this.toggleAttribute("data-theme-dark", this.themeController.theme === "dark");
|
|
@@ -5003,6 +5577,6 @@ __decorate([state()], ZitadelSession.prototype, "loading", null);
|
|
|
5003
5577
|
__decorate([state()], ZitadelSession.prototype, "errorMessage", null);
|
|
5004
5578
|
ZitadelSession = __decorate([customElement("zitadel-session")], ZitadelSession);
|
|
5005
5579
|
//#endregion
|
|
5006
|
-
export {
|
|
5580
|
+
export { getCurrentStep as C, zitadelTrustmarkInnerHtml as D, ZITADEL_ATTRIBUTION_LOGOTYPE_SVG as E, validateBranding as S, submitStep as T, MANDATORY_GATES_MARKER as _, TEMPLATE_NAMES as a, publishedSides as b, applyBrandingTokens as c, createSanitiser as d, builtinLocales as f, en as g, de as h, layout_chrome_default as i, buildBrandingStylesheet as l, it as m, ZitadelLogout as n, ThemeController as o, businessLocales as p, ZitadelLogin as r, applyFontUrl as s, ZitadelSession as t, resolveTheme as u, mandatoryGatesMarkerComment as v, startFlow as w, resolveLogoUrl as x, patchMandatoryGates as y };
|
|
5007
5581
|
|
|
5008
|
-
//# sourceMappingURL=orchestrator-
|
|
5582
|
+
//# sourceMappingURL=orchestrator-BVmKaCqz.mjs.map
|