@webiny/app-admin 6.5.0-beta.9 → 6.5.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/base/Base/DefaultFieldRenderers.js +4 -0
  2. package/base/Base/DefaultFieldRenderers.js.map +1 -1
  3. package/base/Base/FieldRenderers/BirthdayInputRenderer.d.ts +21 -0
  4. package/base/Base/FieldRenderers/BirthdayInputRenderer.js +39 -0
  5. package/base/Base/FieldRenderers/BirthdayInputRenderer.js.map +1 -0
  6. package/base/Base/FieldRenderers/BirthdayInputRenderer.test.d.ts +1 -0
  7. package/base/Base/FieldRenderers/BirthdayInputRenderer.test.js +123 -0
  8. package/base/Base/FieldRenderers/BirthdayInputRenderer.test.js.map +1 -0
  9. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.d.ts +2 -0
  10. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js +13 -5
  11. package/base/Base/FieldRenderers/ObjectRenderer/ObjectFieldComponents.js.map +1 -1
  12. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js +8 -26
  13. package/base/Base/FieldRenderers/ObjectRenderer/ObjectRenderer.js.map +1 -1
  14. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.d.ts +8 -3
  15. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js +25 -17
  16. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.js.map +1 -1
  17. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.test.d.ts +1 -0
  18. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.test.js +106 -0
  19. package/base/Base/FieldRenderers/ObjectRenderer/resolveItemTitle.test.js.map +1 -0
  20. package/base/Base/UserMenu/SwitchTenant.d.ts +20 -0
  21. package/base/Base/UserMenu/SwitchTenant.js +45 -0
  22. package/base/Base/UserMenu/SwitchTenant.js.map +1 -0
  23. package/base/Base/UserMenu.js +4 -0
  24. package/base/Base/UserMenu.js.map +1 -1
  25. package/domain/Identity.d.ts +8 -0
  26. package/domain/Identity.js +6 -0
  27. package/domain/Identity.js.map +1 -1
  28. package/exports/admin/security.d.ts +4 -1
  29. package/exports/admin/security.js +4 -1
  30. package/features/formModel/FieldBuilder.test.js +126 -1
  31. package/features/formModel/FieldBuilder.test.js.map +1 -1
  32. package/features/formModel/abstractions.d.ts +16 -0
  33. package/features/formModel/abstractions.js +2 -1
  34. package/features/formModel/abstractions.js.map +1 -1
  35. package/features/formModel/fieldTypes/DateTimeFieldType.d.ts +65 -4
  36. package/features/formModel/fieldTypes/DateTimeFieldType.js +70 -8
  37. package/features/formModel/fieldTypes/DateTimeFieldType.js.map +1 -1
  38. package/features/formModel/fieldTypes/dateLimits.d.ts +37 -0
  39. package/features/formModel/fieldTypes/dateLimits.js +64 -0
  40. package/features/formModel/fieldTypes/dateLimits.js.map +1 -0
  41. package/features/formModel/fieldTypes/dateLimits.test.d.ts +1 -0
  42. package/features/formModel/fieldTypes/dateLimits.test.js +60 -0
  43. package/features/formModel/fieldTypes/dateLimits.test.js.map +1 -0
  44. package/features/formModel/index.d.ts +2 -2
  45. package/features/formModel/index.js +1 -1
  46. package/features/formModel/renderers.d.ts +1 -0
  47. package/features/formModel/renderers.js +1 -0
  48. package/features/security/LogIn/LogInError.d.ts +15 -0
  49. package/features/security/LogIn/LogInError.js +25 -0
  50. package/features/security/LogIn/LogInError.js.map +1 -0
  51. package/features/security/LogIn/LogInGateway.d.ts +2 -2
  52. package/features/security/LogIn/LogInGateway.js +22 -5
  53. package/features/security/LogIn/LogInGateway.js.map +1 -1
  54. package/features/security/LogIn/LogInRepository.d.ts +1 -2
  55. package/features/security/LogIn/LogInRepository.js +6 -2
  56. package/features/security/LogIn/LogInRepository.js.map +1 -1
  57. package/features/security/LogIn/LogInUseCase.d.ts +7 -2
  58. package/features/security/LogIn/LogInUseCase.js +53 -3
  59. package/features/security/LogIn/LogInUseCase.js.map +1 -1
  60. package/features/security/LogIn/LogInUseCase.test.d.ts +1 -0
  61. package/features/security/LogIn/LogInUseCase.test.js +321 -0
  62. package/features/security/LogIn/LogInUseCase.test.js.map +1 -0
  63. package/features/security/LogIn/abstractions.d.ts +48 -3
  64. package/features/security/LogIn/abstractions.js.map +1 -1
  65. package/features/security/LogIn/createLoginMutation.js +40 -27
  66. package/features/security/LogIn/createLoginMutation.js.map +1 -1
  67. package/features/security/LogIn/feature.js +2 -0
  68. package/features/security/LogIn/feature.js.map +1 -1
  69. package/features/security/LogIn/index.d.ts +1 -0
  70. package/features/security/LogIn/index.js +1 -0
  71. package/features/security/LoginTenantResolver/LoginTenantResolver.d.ts +14 -0
  72. package/features/security/LoginTenantResolver/LoginTenantResolver.js +52 -0
  73. package/features/security/LoginTenantResolver/LoginTenantResolver.js.map +1 -0
  74. package/features/security/LoginTenantResolver/abstractions.d.ts +38 -0
  75. package/features/security/LoginTenantResolver/abstractions.js +5 -0
  76. package/features/security/LoginTenantResolver/abstractions.js.map +1 -0
  77. package/features/security/LoginTenantResolver/feature.d.ts +3 -0
  78. package/features/security/LoginTenantResolver/feature.js +17 -0
  79. package/features/security/LoginTenantResolver/feature.js.map +1 -0
  80. package/features/security/LoginTenantResolver/index.d.ts +2 -0
  81. package/features/security/LoginTenantResolver/index.js +2 -0
  82. package/package.json +24 -24
  83. package/presentation/security/components/LoginNoTenantAccess.d.ts +27 -0
  84. package/presentation/security/components/LoginNoTenantAccess.js +22 -0
  85. package/presentation/security/components/LoginNoTenantAccess.js.map +1 -0
  86. package/presentation/security/components/LoginTenantPicker.d.ts +30 -0
  87. package/presentation/security/components/LoginTenantPicker.js +32 -0
  88. package/presentation/security/components/LoginTenantPicker.js.map +1 -0
  89. package/presentation/security/hooks/useAuthentication.d.ts +2 -2
  90. package/presentation/security/hooks/useAuthentication.js.map +1 -1
@@ -0,0 +1,64 @@
1
+ import { z } from "zod";
2
+ import { getLocalToday, resolveDateBound } from "@webiny/utils";
3
+ const describeBound = (bound)=>"string" == typeof bound ? bound : `"${bound.expression}"`;
4
+ function resolveDateLimits(limits, today = getLocalToday()) {
5
+ const resolve = (bounds = [])=>bounds.flatMap((bound)=>{
6
+ try {
7
+ return [
8
+ resolveDateBound(bound, today)
9
+ ];
10
+ } catch {
11
+ return [];
12
+ }
13
+ });
14
+ const gte = resolve(limits?.gte);
15
+ const lte = resolve(limits?.lte);
16
+ return {
17
+ min: gte.length ? gte.reduce((a, b)=>a > b ? a : b) : void 0,
18
+ max: lte.length ? lte.reduce((a, b)=>a < b ? a : b) : void 0
19
+ };
20
+ }
21
+ function getDateLimits(rendererSettings) {
22
+ if (!rendererSettings || "object" != typeof rendererSettings) return;
23
+ return rendererSettings.dateLimits;
24
+ }
25
+ const DEFAULT_DATE_GTE_MESSAGE = "Must be on or after {date}.";
26
+ const DEFAULT_DATE_LTE_MESSAGE = "Must be on or before {date}.";
27
+ function formatDateLimitMessage(message, date) {
28
+ return message.replace(/\{date}/g, date);
29
+ }
30
+ function createDateLimitsSchema(gte, lte, formatDate = (date)=>date) {
31
+ return z.any().superRefine((value, ctx)=>{
32
+ const values = (Array.isArray(value) ? value : [
33
+ value
34
+ ]).filter((v)=>null != v && "" !== v);
35
+ if (0 === values.length) return;
36
+ const today = getLocalToday();
37
+ const check = (limit, kind)=>{
38
+ let date;
39
+ try {
40
+ date = resolveDateBound(limit.bound, today);
41
+ } catch {
42
+ ctx.addIssue({
43
+ code: "custom",
44
+ message: `Invalid date limit ${describeBound(limit.bound)}.`
45
+ });
46
+ return false;
47
+ }
48
+ const ok = values.every((v)=>"gte" === kind ? String(v) >= date : String(v) <= date);
49
+ if (!ok) {
50
+ const message = limit.message || ("gte" === kind ? DEFAULT_DATE_GTE_MESSAGE : DEFAULT_DATE_LTE_MESSAGE);
51
+ ctx.addIssue({
52
+ code: "custom",
53
+ message: formatDateLimitMessage(message, formatDate(date))
54
+ });
55
+ }
56
+ return ok;
57
+ };
58
+ for (const limit of gte)if (!check(limit, "gte")) return;
59
+ for (const limit of lte)if (!check(limit, "lte")) return;
60
+ });
61
+ }
62
+ export { DEFAULT_DATE_GTE_MESSAGE, DEFAULT_DATE_LTE_MESSAGE, createDateLimitsSchema, formatDateLimitMessage, getDateLimits, resolveDateLimits };
63
+
64
+ //# sourceMappingURL=dateLimits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/formModel/fieldTypes/dateLimits.js","sources":["../../../../src/features/formModel/fieldTypes/dateLimits.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { getLocalToday, resolveDateBound, type DateBound } from \"@webiny/utils\";\n\nexport type { DateBound };\n\nexport interface IDateLimit {\n bound: DateBound;\n message?: string;\n}\n\n/** A field's date limits, as `datetime()` hands them to its renderer in `rendererSettings`. */\nexport interface IDateLimits {\n gte: DateBound[];\n lte: DateBound[];\n}\n\n/** The resolved limits: the latest lower limit and the earliest upper one. */\nexport interface IResolvedDateLimits {\n min?: string;\n max?: string;\n}\n\nconst describeBound = (bound: DateBound) =>\n typeof bound === \"string\" ? bound : `\"${bound.expression}\"`;\n\n/**\n * A field's limits as dates, with expressions worked out against `today` (the editor's local\n * date by default). An expression that can't be read is left out.\n */\nexport function resolveDateLimits(\n limits: IDateLimits | undefined,\n today: string = getLocalToday()\n): IResolvedDateLimits {\n const resolve = (bounds: DateBound[] = []) =>\n bounds.flatMap(bound => {\n try {\n return [resolveDateBound(bound, today)];\n } catch {\n return [];\n }\n });\n const gte = resolve(limits?.gte);\n const lte = resolve(limits?.lte);\n return {\n min: gte.length ? gte.reduce((a, b) => (a > b ? a : b)) : undefined,\n max: lte.length ? lte.reduce((a, b) => (a < b ? a : b)) : undefined\n };\n}\n\n/** Reads the limits `datetime()` put into a field's renderer settings. */\nexport function getDateLimits(rendererSettings: unknown): IDateLimits | undefined {\n if (!rendererSettings || typeof rendererSettings !== \"object\") {\n return undefined;\n }\n return (rendererSettings as { dateLimits?: IDateLimits }).dateLimits;\n}\n\n/** Shows a worked-out limit in a message; by default as it is stored (`2008-10-19`). */\nexport type DateLimitFormatter = (date: string) => string;\n\n/** The message when a limit has none of its own. */\nexport const DEFAULT_DATE_GTE_MESSAGE = \"Must be on or after {date}.\";\nexport const DEFAULT_DATE_LTE_MESSAGE = \"Must be on or before {date}.\";\n\n/** Puts the worked-out limit into a message's `{date}`. */\nexport function formatDateLimitMessage(message: string, date: string): string {\n return message.replace(/\\{date}/g, date);\n}\n\n/**\n * A schema that checks a value, or each value of a list, against the limits. Expressions are\n * worked out against the editor's local date at the moment of the check, so a form left open\n * overnight checks against the new day. A message's `{date}` is the worked-out limit.\n */\nexport function createDateLimitsSchema(\n gte: IDateLimit[],\n lte: IDateLimit[],\n formatDate: DateLimitFormatter = date => date\n): z.ZodTypeAny {\n return z.any().superRefine((value, ctx) => {\n const values = (Array.isArray(value) ? value : [value]).filter(\n v => v !== null && v !== undefined && v !== \"\"\n );\n if (values.length === 0) {\n return;\n }\n const today = getLocalToday();\n const check = (limit: IDateLimit, kind: \"gte\" | \"lte\") => {\n let date: string;\n try {\n date = resolveDateBound(limit.bound, today);\n } catch {\n ctx.addIssue({\n code: \"custom\",\n message: `Invalid date limit ${describeBound(limit.bound)}.`\n });\n return false;\n }\n const ok = values.every(v => (kind === \"gte\" ? String(v) >= date : String(v) <= date));\n if (!ok) {\n const message =\n limit.message ||\n (kind === \"gte\" ? DEFAULT_DATE_GTE_MESSAGE : DEFAULT_DATE_LTE_MESSAGE);\n ctx.addIssue({\n code: \"custom\",\n message: formatDateLimitMessage(message, formatDate(date))\n });\n }\n return ok;\n };\n for (const limit of gte) {\n if (!check(limit, \"gte\")) {\n return;\n }\n }\n for (const limit of lte) {\n if (!check(limit, \"lte\")) {\n return;\n }\n }\n });\n}\n"],"names":["describeBound","bound","resolveDateLimits","limits","today","getLocalToday","resolve","bounds","resolveDateBound","gte","lte","a","b","undefined","getDateLimits","rendererSettings","DEFAULT_DATE_GTE_MESSAGE","DEFAULT_DATE_LTE_MESSAGE","formatDateLimitMessage","message","date","createDateLimitsSchema","formatDate","z","value","ctx","values","Array","v","check","limit","kind","ok","String"],"mappings":";;AAsBA,MAAMA,gBAAgB,CAACC,QACnB,AAAiB,YAAjB,OAAOA,QAAqBA,QAAQ,CAAC,CAAC,EAAEA,MAAM,UAAU,CAAC,CAAC,CAAC;AAMxD,SAASC,kBACZC,MAA+B,EAC/BC,QAAgBC,eAAe;IAE/B,MAAMC,UAAU,CAACC,SAAsB,EAAE,GACrCA,OAAO,OAAO,CAACN,CAAAA;YACX,IAAI;gBACA,OAAO;oBAACO,iBAAiBP,OAAOG;iBAAO;YAC3C,EAAE,OAAM;gBACJ,OAAO,EAAE;YACb;QACJ;IACJ,MAAMK,MAAMH,QAAQH,QAAQ;IAC5B,MAAMO,MAAMJ,QAAQH,QAAQ;IAC5B,OAAO;QACH,KAAKM,IAAI,MAAM,GAAGA,IAAI,MAAM,CAAC,CAACE,GAAGC,IAAOD,IAAIC,IAAID,IAAIC,KAAMC;QAC1D,KAAKH,IAAI,MAAM,GAAGA,IAAI,MAAM,CAAC,CAACC,GAAGC,IAAOD,IAAIC,IAAID,IAAIC,KAAMC;IAC9D;AACJ;AAGO,SAASC,cAAcC,gBAAyB;IACnD,IAAI,CAACA,oBAAoB,AAA4B,YAA5B,OAAOA,kBAC5B;IAEJ,OAAQA,iBAAkD,UAAU;AACxE;AAMO,MAAMC,2BAA2B;AACjC,MAAMC,2BAA2B;AAGjC,SAASC,uBAAuBC,OAAe,EAAEC,IAAY;IAChE,OAAOD,QAAQ,OAAO,CAAC,YAAYC;AACvC;AAOO,SAASC,uBACZZ,GAAiB,EACjBC,GAAiB,EACjBY,aAAiCF,CAAAA,OAAQA,IAAI;IAE7C,OAAOG,EAAE,GAAG,GAAG,WAAW,CAAC,CAACC,OAAOC;QAC/B,MAAMC,SAAUC,AAAAA,CAAAA,MAAM,OAAO,CAACH,SAASA,QAAQ;YAACA;SAAK,A,EAAG,MAAM,CAC1DI,CAAAA,IAAKA,QAAAA,KAAiCA,AAAM,OAANA;QAE1C,IAAIF,AAAkB,MAAlBA,OAAO,MAAM,EACb;QAEJ,MAAMtB,QAAQC;QACd,MAAMwB,QAAQ,CAACC,OAAmBC;YAC9B,IAAIX;YACJ,IAAI;gBACAA,OAAOZ,iBAAiBsB,MAAM,KAAK,EAAE1B;YACzC,EAAE,OAAM;gBACJqB,IAAI,QAAQ,CAAC;oBACT,MAAM;oBACN,SAAS,CAAC,mBAAmB,EAAEzB,cAAc8B,MAAM,KAAK,EAAE,CAAC,CAAC;gBAChE;gBACA,OAAO;YACX;YACA,MAAME,KAAKN,OAAO,KAAK,CAACE,CAAAA,IAAMG,AAAS,UAATA,OAAiBE,OAAOL,MAAMR,OAAOa,OAAOL,MAAMR;YAChF,IAAI,CAACY,IAAI;gBACL,MAAMb,UACFW,MAAM,OAAO,IACZC,CAAAA,AAAS,UAATA,OAAiBf,2BAA2BC,wBAAuB;gBACxEQ,IAAI,QAAQ,CAAC;oBACT,MAAM;oBACN,SAASP,uBAAuBC,SAASG,WAAWF;gBACxD;YACJ;YACA,OAAOY;QACX;QACA,KAAK,MAAMF,SAASrB,IAChB,IAAI,CAACoB,MAAMC,OAAO,QACd;QAGR,KAAK,MAAMA,SAASpB,IAChB,IAAI,CAACmB,MAAMC,OAAO,QACd;IAGZ;AACJ"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,60 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getDateLimits, resolveDateLimits } from "./dateLimits.js";
3
+ describe("resolveDateLimits", ()=>{
4
+ it("takes the latest lower limit and the earliest upper one", ()=>{
5
+ expect(resolveDateLimits({
6
+ gte: [
7
+ "1900-01-01",
8
+ {
9
+ expression: "today-100y"
10
+ }
11
+ ],
12
+ lte: [
13
+ {
14
+ expression: "today-18y"
15
+ },
16
+ "2020-01-01"
17
+ ]
18
+ }, "2026-10-19")).toEqual({
19
+ min: "1926-10-19",
20
+ max: "2008-10-19"
21
+ });
22
+ });
23
+ it("leaves out a side without limits and an expression it can't read", ()=>{
24
+ expect(resolveDateLimits({
25
+ gte: [],
26
+ lte: [
27
+ {
28
+ expression: "nope"
29
+ }
30
+ ]
31
+ }, "2026-10-19")).toEqual({
32
+ min: void 0,
33
+ max: void 0
34
+ });
35
+ expect(resolveDateLimits(void 0, "2026-10-19")).toEqual({
36
+ min: void 0,
37
+ max: void 0
38
+ });
39
+ });
40
+ });
41
+ describe("getDateLimits", ()=>{
42
+ it("reads the limits from renderer settings", ()=>{
43
+ const dateLimits = {
44
+ gte: [
45
+ "2000-01-01"
46
+ ],
47
+ lte: []
48
+ };
49
+ expect(getDateLimits({
50
+ type: "date",
51
+ dateLimits
52
+ })).toBe(dateLimits);
53
+ expect(getDateLimits({
54
+ type: "date"
55
+ })).toBeUndefined();
56
+ expect(getDateLimits(void 0)).toBeUndefined();
57
+ });
58
+ });
59
+
60
+ //# sourceMappingURL=dateLimits.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/formModel/fieldTypes/dateLimits.test.js","sources":["../../../../src/features/formModel/fieldTypes/dateLimits.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { getDateLimits, resolveDateLimits } from \"./dateLimits.js\";\n\ndescribe(\"resolveDateLimits\", () => {\n it(\"takes the latest lower limit and the earliest upper one\", () => {\n expect(\n resolveDateLimits(\n {\n gte: [\"1900-01-01\", { expression: \"today-100y\" }],\n lte: [{ expression: \"today-18y\" }, \"2020-01-01\"]\n },\n \"2026-10-19\"\n )\n ).toEqual({ min: \"1926-10-19\", max: \"2008-10-19\" });\n });\n\n it(\"leaves out a side without limits and an expression it can't read\", () => {\n expect(resolveDateLimits({ gte: [], lte: [{ expression: \"nope\" }] }, \"2026-10-19\")).toEqual(\n { min: undefined, max: undefined }\n );\n expect(resolveDateLimits(undefined, \"2026-10-19\")).toEqual({\n min: undefined,\n max: undefined\n });\n });\n});\n\ndescribe(\"getDateLimits\", () => {\n it(\"reads the limits from renderer settings\", () => {\n const dateLimits = { gte: [\"2000-01-01\"], lte: [] };\n expect(getDateLimits({ type: \"date\", dateLimits })).toBe(dateLimits);\n expect(getDateLimits({ type: \"date\" })).toBeUndefined();\n expect(getDateLimits(undefined)).toBeUndefined();\n });\n});\n"],"names":["describe","it","expect","resolveDateLimits","undefined","dateLimits","getDateLimits"],"mappings":";;AAGAA,SAAS,qBAAqB;IAC1BC,GAAG,2DAA2D;QAC1DC,OACIC,kBACI;YACI,KAAK;gBAAC;gBAAc;oBAAE,YAAY;gBAAa;aAAE;YACjD,KAAK;gBAAC;oBAAE,YAAY;gBAAY;gBAAG;aAAa;QACpD,GACA,eAEN,OAAO,CAAC;YAAE,KAAK;YAAc,KAAK;QAAa;IACrD;IAEAF,GAAG,oEAAoE;QACnEC,OAAOC,kBAAkB;YAAE,KAAK,EAAE;YAAE,KAAK;gBAAC;oBAAE,YAAY;gBAAO;aAAE;QAAC,GAAG,eAAe,OAAO,CACvF;YAAE,KAAKC;YAAW,KAAKA;QAAU;QAErCF,OAAOC,kBAAkBC,QAAW,eAAe,OAAO,CAAC;YACvD,KAAKA;YACL,KAAKA;QACT;IACJ;AACJ;AAEAJ,SAAS,iBAAiB;IACtBC,GAAG,2CAA2C;QAC1C,MAAMI,aAAa;YAAE,KAAK;gBAAC;aAAa;YAAE,KAAK,EAAE;QAAC;QAClDH,OAAOI,cAAc;YAAE,MAAM;YAAQD;QAAW,IAAI,IAAI,CAACA;QACzDH,OAAOI,cAAc;YAAE,MAAM;QAAO,IAAI,aAAa;QACrDJ,OAAOI,cAAcF,SAAY,aAAa;IAClD;AACJ"}
@@ -1,7 +1,7 @@
1
1
  export { FormModelFeature } from "./feature.js";
2
2
  import "./renderers.js";
3
- export { FormModelFactory, RuleEvaluator, FieldType, FieldBuilderRegistry } from "./abstractions.js";
4
- export type { FormModel, IFieldRendererRegistry, FieldRendererName, FieldRendererSettings, IFormModelFactory, IFormModelConfig, ILayoutBuilder, ILayoutNodeBuilder, IRowBuilder, ISeparatorBuilder, ITabBuilder, ITabsBuilder, IElementBuilder, IObjectBuilder, IFieldBuilder, IOptionsFieldBuilder, IObjectFieldBuilder, IFieldBuilderRegistry, IFormModel, IField, IObjectField, IObjectFieldConfig, IListItemField, IObjectFieldVM, IObjectFieldItemVM, ITemplateBuilder, ITemplateIcon, ITemplateConfig, ITemplateVM, IObjectFieldTemplates, FieldTypeMap, ITypedField, IFieldConfig, IFieldVM, IFieldValidation, IFormVM, IFormError, IValueOption, IRowNode, IRowNodeHandle, IRowNodeVM, ISeparatorNode, ISeparatorNodeVM, IAlertNode, IAlertNodeVM, ITabsNode, ITabDefinition, ITabsNodeVM, ITabDefinitionVM, IElementNode, IElementNodeVM, IObjectNode, ILayoutNodeAccessHandle, LayoutNodeHandleMap, ITabsHandle, ITabHandle, LayoutNode, LayoutNodeVM, LayoutPosition, IPositionedLayoutNode, ILayoutNodeHandle, ILayoutModifier, IFormModifier, IRule, IRuleEvaluator, RuleAction, BeforeChangeCallback, AfterChangeCallback, AfterSetValueCallback, OnBlurCallback, CloneValueCallback, IFieldTypeFactory, FileValue } from "./abstractions.js";
3
+ export { FormModelFactory, RuleEvaluator, FieldType, FieldValueFormatter, FieldBuilderRegistry } from "./abstractions.js";
4
+ export type { FormModel, IFieldRendererRegistry, FieldRendererName, FieldRendererSettings, IFormModelFactory, IFormModelConfig, ILayoutBuilder, ILayoutNodeBuilder, IRowBuilder, ISeparatorBuilder, ITabBuilder, ITabsBuilder, IElementBuilder, IObjectBuilder, IFieldBuilder, IOptionsFieldBuilder, IObjectFieldBuilder, IFieldBuilderRegistry, IFormModel, IField, IObjectField, IObjectFieldConfig, IListItemField, IObjectFieldVM, IObjectFieldItemVM, ITemplateBuilder, ITemplateIcon, ITemplateConfig, ITemplateVM, IObjectFieldTemplates, FieldTypeMap, ITypedField, IFieldConfig, IFieldVM, IFieldValueFormatter, IFieldValidation, IFormVM, IFormError, IValueOption, IRowNode, IRowNodeHandle, IRowNodeVM, ISeparatorNode, ISeparatorNodeVM, IAlertNode, IAlertNodeVM, ITabsNode, ITabDefinition, ITabsNodeVM, ITabDefinitionVM, IElementNode, IElementNodeVM, IObjectNode, ILayoutNodeAccessHandle, LayoutNodeHandleMap, ITabsHandle, ITabHandle, LayoutNode, LayoutNodeVM, LayoutPosition, IPositionedLayoutNode, ILayoutNodeHandle, ILayoutModifier, IFormModifier, IRule, IRuleEvaluator, RuleAction, BeforeChangeCallback, AfterChangeCallback, AfterSetValueCallback, OnBlurCallback, CloneValueCallback, IFieldTypeFactory, FileValue } from "./abstractions.js";
5
5
  export { Field } from "./Field.js";
6
6
  export { FieldBuilder, createFieldBuilderRegistry } from "./FieldBuilder.js";
7
7
  import "./fieldTypes/TextFieldType.js";
@@ -7,7 +7,7 @@ import "./fieldTypes/TextFieldType.js";
7
7
  import "./fieldTypes/NumberFieldType.js";
8
8
  import "./fieldTypes/BooleanFieldType.js";
9
9
  export { FormModelFeature } from "./feature.js";
10
- export { FieldBuilderRegistry, FieldType, FormModelFactory, RuleEvaluator } from "./abstractions.js";
10
+ export { FieldBuilderRegistry, FieldType, FieldValueFormatter, FormModelFactory, RuleEvaluator } from "./abstractions.js";
11
11
  export { Field } from "./Field.js";
12
12
  export { FieldBuilder, createFieldBuilderRegistry } from "./FieldBuilder.js";
13
13
  export { BooleanFieldBuilder, BooleanFieldType, DateTimeFieldBuilder, DateTimeFieldType, FileFieldBuilder, FileFieldType, FileUrlFieldBuilder, FileUrlFieldType, NumberFieldBuilder, NumberFieldType, ObjectFieldBuilder, ObjectFieldType, TextFieldBuilder, TextFieldType } from "./fieldTypes/index.js";
@@ -16,6 +16,7 @@ import "../../base/Base/FieldRenderers/RadioButtonsRenderer.js";
16
16
  import "../../base/Base/FieldRenderers/CheckboxesRenderer.js";
17
17
  import "../../base/Base/FieldRenderers/DateTimeRenderer.js";
18
18
  import "../../base/Base/FieldRenderers/DateTimeInputsRenderer.js";
19
+ import "../../base/Base/FieldRenderers/BirthdayInputRenderer.js";
19
20
  import "../../base/Base/FieldRenderers/HiddenRenderer.js";
20
21
  import "../../base/Base/FieldRenderers/FilePickerRenderer.js";
21
22
  import "../../base/Base/FieldRenderers/CodeEditorRenderer.js";
@@ -16,6 +16,7 @@ import "../../base/Base/FieldRenderers/RadioButtonsRenderer.js";
16
16
  import "../../base/Base/FieldRenderers/CheckboxesRenderer.js";
17
17
  import "../../base/Base/FieldRenderers/DateTimeRenderer.js";
18
18
  import "../../base/Base/FieldRenderers/DateTimeInputsRenderer.js";
19
+ import "../../base/Base/FieldRenderers/BirthdayInputRenderer.js";
19
20
  import "../../base/Base/FieldRenderers/HiddenRenderer.js";
20
21
  import "../../base/Base/FieldRenderers/FilePickerRenderer.js";
21
22
  import "../../base/Base/FieldRenderers/CodeEditorRenderer.js";
@@ -0,0 +1,15 @@
1
+ export interface LogInErrorParams {
2
+ code?: string | null;
3
+ message: string;
4
+ data?: Record<string, any> | null;
5
+ }
6
+ /**
7
+ * A failed `security.login`: either the error the API returned, or the request's own failure
8
+ * (for example, the tenant in the `x-tenant` header couldn't be loaded).
9
+ */
10
+ export declare class LogInError extends Error {
11
+ readonly code: string;
12
+ readonly data: Record<string, any>;
13
+ constructor(params: LogInErrorParams);
14
+ static from(error: unknown): LogInError;
15
+ }
@@ -0,0 +1,25 @@
1
+ class LogInError extends Error {
2
+ constructor(params){
3
+ super(params.message);
4
+ this.name = "LogInError";
5
+ this.code = params.code ?? "";
6
+ this.data = params.data ?? {};
7
+ }
8
+ static from(error) {
9
+ if (error instanceof LogInError) return error;
10
+ if (error && "object" == typeof error) {
11
+ const { code, message, data } = error;
12
+ return new LogInError({
13
+ code,
14
+ message: message || "Login failed.",
15
+ data
16
+ });
17
+ }
18
+ return new LogInError({
19
+ message: String(error)
20
+ });
21
+ }
22
+ }
23
+ export { LogInError };
24
+
25
+ //# sourceMappingURL=LogInError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/security/LogIn/LogInError.js","sources":["../../../../src/features/security/LogIn/LogInError.ts"],"sourcesContent":["export interface LogInErrorParams {\n code?: string | null;\n message: string;\n data?: Record<string, any> | null;\n}\n\n/**\n * A failed `security.login`: either the error the API returned, or the request's own failure\n * (for example, the tenant in the `x-tenant` header couldn't be loaded).\n */\nexport class LogInError extends Error {\n public readonly code: string;\n public readonly data: Record<string, any>;\n\n constructor(params: LogInErrorParams) {\n super(params.message);\n this.name = \"LogInError\";\n this.code = params.code ?? \"\";\n this.data = params.data ?? {};\n }\n\n static from(error: unknown): LogInError {\n if (error instanceof LogInError) {\n return error;\n }\n\n if (error && typeof error === \"object\") {\n const { code, message, data } = error as Partial<LogInErrorParams>;\n return new LogInError({\n code,\n message: message || \"Login failed.\",\n data\n });\n }\n\n return new LogInError({ message: String(error) });\n }\n}\n"],"names":["LogInError","Error","params","error","code","message","data","String"],"mappings":"AAUO,MAAMA,mBAAmBC;IAI5B,YAAYC,MAAwB,CAAE;QAClC,KAAK,CAACA,OAAO,OAAO;QACpB,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,IAAI,GAAGA,OAAO,IAAI,IAAI;QAC3B,IAAI,CAAC,IAAI,GAAGA,OAAO,IAAI,IAAI,CAAC;IAChC;IAEA,OAAO,KAAKC,KAAc,EAAc;QACpC,IAAIA,iBAAiBH,YACjB,OAAOG;QAGX,IAAIA,SAAS,AAAiB,YAAjB,OAAOA,OAAoB;YACpC,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAEC,IAAI,EAAE,GAAGH;YAChC,OAAO,IAAIH,WAAW;gBAClBI;gBACA,SAASC,WAAW;gBACpBC;YACJ;QACJ;QAEA,OAAO,IAAIN,WAAW;YAAE,SAASO,OAAOJ;QAAO;IACnD;AACJ"}
@@ -1,9 +1,9 @@
1
1
  import { MainGraphQLClient } from "@webiny/app/features/mainGraphQLClient/index.js";
2
- import { LogInGateway as Abstraction, LogInRepository } from "./abstractions.js";
2
+ import { LogInGateway as Abstraction } from "./abstractions.js";
3
3
  declare class LogInGatewayImpl implements Abstraction.Interface {
4
4
  private graphqlClient;
5
5
  constructor(graphqlClient: MainGraphQLClient.Interface);
6
- execute(): Promise<LogInRepository.IdentityDTO>;
6
+ execute(): Promise<Abstraction.Result>;
7
7
  }
8
8
  export declare const LogInGateway: typeof LogInGatewayImpl & {
9
9
  __abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").ILogInGateway>;
@@ -1,17 +1,34 @@
1
1
  import { MainGraphQLClient } from "@webiny/app/features/mainGraphQLClient/index.js";
2
2
  import { LogInGateway } from "./abstractions.js";
3
3
  import { createLoginMutation } from "./createLoginMutation.js";
4
+ import { LogInError } from "./LogInError.js";
4
5
  class LogInGatewayImpl {
5
6
  constructor(graphqlClient){
6
7
  this.graphqlClient = graphqlClient;
7
8
  }
8
9
  async execute() {
9
- const response = await this.graphqlClient.execute({
10
- query: createLoginMutation()
11
- });
10
+ let response;
11
+ try {
12
+ response = await this.graphqlClient.execute({
13
+ query: createLoginMutation()
14
+ });
15
+ } catch (error) {
16
+ throw LogInError.from(error);
17
+ }
12
18
  const { data, error } = response.security.login;
13
- if (error) throw new Error(error.message);
14
- return data;
19
+ if (error) throw new LogInError(error);
20
+ if (!data) throw new LogInError({
21
+ message: "Login returned no identity."
22
+ });
23
+ if ("SecurityLoginTenantSelection" === data.__typename) return {
24
+ step: "selectTenant",
25
+ tenants: data.tenants
26
+ };
27
+ const { __typename, ...identity } = data;
28
+ return {
29
+ step: "done",
30
+ identity
31
+ };
15
32
  }
16
33
  }
17
34
  const LogInGateway_LogInGateway = LogInGateway.createImplementation({
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/LogIn/LogInGateway.js","sources":["../../../../src/features/security/LogIn/LogInGateway.ts"],"sourcesContent":["import { MainGraphQLClient } from \"@webiny/app/features/mainGraphQLClient/index.js\";\nimport { LogInGateway as Abstraction, LogInRepository } from \"./abstractions.js\";\nimport { createLoginMutation } from \"./createLoginMutation.js\";\n\ninterface LoginMutationResponse {\n security: {\n login: {\n data: LogInRepository.IdentityDTO;\n error: {\n message: string;\n };\n };\n };\n}\n\nclass LogInGatewayImpl implements Abstraction.Interface {\n constructor(private graphqlClient: MainGraphQLClient.Interface) {}\n\n async execute(): Promise<LogInRepository.IdentityDTO> {\n const response = await this.graphqlClient.execute<LoginMutationResponse>({\n query: createLoginMutation()\n });\n\n const { data, error } = response.security.login;\n\n if (error) {\n throw new Error(error.message);\n }\n\n return data;\n }\n}\n\nexport const LogInGateway = Abstraction.createImplementation({\n implementation: LogInGatewayImpl,\n dependencies: [MainGraphQLClient]\n});\n"],"names":["LogInGatewayImpl","graphqlClient","response","createLoginMutation","data","error","Error","LogInGateway","Abstraction","MainGraphQLClient"],"mappings":";;;AAeA,MAAMA;IACF,YAAoBC,aAA0C,CAAE;aAA5CA,aAAa,GAAbA;IAA6C;IAEjE,MAAM,UAAgD;QAClD,MAAMC,WAAW,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAwB;YACrE,OAAOC;QACX;QAEA,MAAM,EAAEC,IAAI,EAAEC,KAAK,EAAE,GAAGH,SAAS,QAAQ,CAAC,KAAK;QAE/C,IAAIG,OACA,MAAM,IAAIC,MAAMD,MAAM,OAAO;QAGjC,OAAOD;IACX;AACJ;AAEO,MAAMG,4BAAeC,aAAAA,oBAAgC,CAAC;IACzD,gBAAgBR;IAChB,cAAc;QAACS;KAAkB;AACrC"}
1
+ {"version":3,"file":"features/security/LogIn/LogInGateway.js","sources":["../../../../src/features/security/LogIn/LogInGateway.ts"],"sourcesContent":["import { MainGraphQLClient } from \"@webiny/app/features/mainGraphQLClient/index.js\";\nimport type { Tenant } from \"~/features/tenancy/abstractions.js\";\nimport { LogInGateway as Abstraction, LogInRepository } from \"./abstractions.js\";\nimport { createLoginMutation } from \"./createLoginMutation.js\";\nimport { LogInError } from \"./LogInError.js\";\n\ntype LoginResultDTO =\n | ({ __typename: \"SecurityIdentity\" } & LogInRepository.IdentityDTO)\n | { __typename: \"SecurityLoginTenantSelection\"; tenants: Tenant[] };\n\ninterface LoginMutationResponse {\n security: {\n login: {\n data: LoginResultDTO | null;\n error: {\n code: string;\n message: string;\n data: Record<string, any> | null;\n };\n };\n };\n}\n\nclass LogInGatewayImpl implements Abstraction.Interface {\n constructor(private graphqlClient: MainGraphQLClient.Interface) {}\n\n async execute(): Promise<Abstraction.Result> {\n let response: LoginMutationResponse;\n try {\n response = await this.graphqlClient.execute<LoginMutationResponse>({\n query: createLoginMutation()\n });\n } catch (error) {\n throw LogInError.from(error);\n }\n\n const { data, error } = response.security.login;\n\n if (error) {\n throw new LogInError(error);\n }\n\n if (!data) {\n throw new LogInError({ message: \"Login returned no identity.\" });\n }\n\n if (data.__typename === \"SecurityLoginTenantSelection\") {\n return { step: \"selectTenant\", tenants: data.tenants };\n }\n\n const { __typename, ...identity } = data;\n return { step: \"done\", identity };\n }\n}\n\nexport const LogInGateway = Abstraction.createImplementation({\n implementation: LogInGatewayImpl,\n dependencies: [MainGraphQLClient]\n});\n"],"names":["LogInGatewayImpl","graphqlClient","response","createLoginMutation","error","LogInError","data","__typename","identity","LogInGateway","Abstraction","MainGraphQLClient"],"mappings":";;;;AAuBA,MAAMA;IACF,YAAoBC,aAA0C,CAAE;aAA5CA,aAAa,GAAbA;IAA6C;IAEjE,MAAM,UAAuC;QACzC,IAAIC;QACJ,IAAI;YACAA,WAAW,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAwB;gBAC/D,OAAOC;YACX;QACJ,EAAE,OAAOC,OAAO;YACZ,MAAMC,WAAW,IAAI,CAACD;QAC1B;QAEA,MAAM,EAAEE,IAAI,EAAEF,KAAK,EAAE,GAAGF,SAAS,QAAQ,CAAC,KAAK;QAE/C,IAAIE,OACA,MAAM,IAAIC,WAAWD;QAGzB,IAAI,CAACE,MACD,MAAM,IAAID,WAAW;YAAE,SAAS;QAA8B;QAGlE,IAAIC,AAAoB,mCAApBA,KAAK,UAAU,EACf,OAAO;YAAE,MAAM;YAAgB,SAASA,KAAK,OAAO;QAAC;QAGzD,MAAM,EAAEC,UAAU,EAAE,GAAGC,UAAU,GAAGF;QACpC,OAAO;YAAE,MAAM;YAAQE;QAAS;IACpC;AACJ;AAEO,MAAMC,4BAAeC,aAAAA,oBAAgC,CAAC;IACzD,gBAAgBV;IAChB,cAAc;QAACW;KAAkB;AACrC"}
@@ -1,4 +1,3 @@
1
- import type { Identity } from "../../../domain/Identity.js";
2
1
  import { LogInRepository as Abstraction } from "./abstractions.js";
3
2
  import { LogInGateway } from "./abstractions.js";
4
3
  import { IdentityMapper } from "./abstractions.js";
@@ -6,7 +5,7 @@ declare class LogInRepositoryImpl implements Abstraction.Interface {
6
5
  private gateway;
7
6
  private mapper;
8
7
  constructor(gateway: LogInGateway.Interface, mapper: IdentityMapper.Interface);
9
- login(): Promise<Identity>;
8
+ login(): Promise<Abstraction.Result>;
10
9
  }
11
10
  export declare const LogInRepository: typeof LogInRepositoryImpl & {
12
11
  __abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").ILogInRepository>;
@@ -5,8 +5,12 @@ class LogInRepositoryImpl {
5
5
  this.mapper = mapper;
6
6
  }
7
7
  async login() {
8
- const dto = await this.gateway.execute();
9
- return this.mapper.toIdentity(dto);
8
+ const result = await this.gateway.execute();
9
+ if ("selectTenant" === result.step) return result;
10
+ return {
11
+ step: "done",
12
+ identity: this.mapper.toIdentity(result.identity)
13
+ };
10
14
  }
11
15
  }
12
16
  const LogInRepository_LogInRepository = LogInRepository.createImplementation({
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/LogIn/LogInRepository.js","sources":["../../../../src/features/security/LogIn/LogInRepository.ts"],"sourcesContent":["import type { Identity } from \"~/domain/Identity.js\";\nimport { LogInRepository as Abstraction } from \"./abstractions.js\";\nimport { LogInGateway } from \"./abstractions.js\";\nimport { IdentityMapper } from \"./abstractions.js\";\n\nclass LogInRepositoryImpl implements Abstraction.Interface {\n constructor(\n private gateway: LogInGateway.Interface,\n private mapper: IdentityMapper.Interface\n ) {}\n\n async login(): Promise<Identity> {\n const dto = await this.gateway.execute();\n return this.mapper.toIdentity(dto);\n }\n}\n\nexport const LogInRepository = Abstraction.createImplementation({\n implementation: LogInRepositoryImpl,\n dependencies: [LogInGateway, IdentityMapper]\n});\n"],"names":["LogInRepositoryImpl","gateway","mapper","dto","LogInRepository","Abstraction","LogInGateway","IdentityMapper"],"mappings":";AAKA,MAAMA;IACF,YACYC,OAA+B,EAC/BC,MAAgC,CAC1C;aAFUD,OAAO,GAAPA;aACAC,MAAM,GAANA;IACT;IAEH,MAAM,QAA2B;QAC7B,MAAMC,MAAM,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO;QACtC,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,CAACA;IAClC;AACJ;AAEO,MAAMC,kCAAkBC,gBAAAA,oBAAgC,CAAC;IAC5D,gBAAgBL;IAChB,cAAc;QAACM;QAAcC;KAAe;AAChD"}
1
+ {"version":3,"file":"features/security/LogIn/LogInRepository.js","sources":["../../../../src/features/security/LogIn/LogInRepository.ts"],"sourcesContent":["import { LogInRepository as Abstraction } from \"./abstractions.js\";\nimport { LogInGateway } from \"./abstractions.js\";\nimport { IdentityMapper } from \"./abstractions.js\";\n\nclass LogInRepositoryImpl implements Abstraction.Interface {\n constructor(\n private gateway: LogInGateway.Interface,\n private mapper: IdentityMapper.Interface\n ) {}\n\n async login(): Promise<Abstraction.Result> {\n const result = await this.gateway.execute();\n if (result.step === \"selectTenant\") {\n return result;\n }\n\n return { step: \"done\", identity: this.mapper.toIdentity(result.identity) };\n }\n}\n\nexport const LogInRepository = Abstraction.createImplementation({\n implementation: LogInRepositoryImpl,\n dependencies: [LogInGateway, IdentityMapper]\n});\n"],"names":["LogInRepositoryImpl","gateway","mapper","result","LogInRepository","Abstraction","LogInGateway","IdentityMapper"],"mappings":";AAIA,MAAMA;IACF,YACYC,OAA+B,EAC/BC,MAAgC,CAC1C;aAFUD,OAAO,GAAPA;aACAC,MAAM,GAANA;IACT;IAEH,MAAM,QAAqC;QACvC,MAAMC,SAAS,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO;QACzC,IAAIA,AAAgB,mBAAhBA,OAAO,IAAI,EACX,OAAOA;QAGX,OAAO;YAAE,MAAM;YAAQ,UAAU,IAAI,CAAC,MAAM,CAAC,UAAU,CAACA,OAAO,QAAQ;QAAE;IAC7E;AACJ;AAEO,MAAMC,kCAAkBC,gBAAAA,oBAAgC,CAAC;IAC5D,gBAAgBL;IAChB,cAAc;QAACM;QAAcC;KAAe;AAChD"}
@@ -1,12 +1,17 @@
1
1
  import { LogInRepository, LogInUseCase as UseCase } from "./abstractions.js";
2
2
  import { AuthenticationContext } from "../../../features/security/AuthenticationContext/index.js";
3
3
  import { IdentityContext } from "../../../features/security/IdentityContext/index.js";
4
+ import { LoginTenantResolver } from "../../../features/security/LoginTenantResolver/index.js";
5
+ import { TenantContext } from "../../../features/tenancy/abstractions.js";
4
6
  declare class LoginUseCaseImpl implements UseCase.Interface {
5
7
  private authContext;
6
8
  private identityContext;
7
9
  private logInRepository;
8
- constructor(authContext: AuthenticationContext.Interface, identityContext: IdentityContext.Interface, logInRepository: LogInRepository.Interface);
9
- execute(params: UseCase.Params): Promise<void>;
10
+ private tenantResolver;
11
+ private tenantContext;
12
+ constructor(authContext: AuthenticationContext.Interface, identityContext: IdentityContext.Interface, logInRepository: LogInRepository.Interface, tenantResolver: LoginTenantResolver.Interface, tenantContext: TenantContext.Interface);
13
+ execute(params: UseCase.Params): Promise<UseCase.Result>;
14
+ private applyResolution;
10
15
  private validatePermissions;
11
16
  }
12
17
  export declare const LogInUseCase: typeof LoginUseCaseImpl & {
@@ -1,11 +1,16 @@
1
1
  import { LogInRepository, LogInUseCase } from "./abstractions.js";
2
2
  import { AuthenticationContext } from "../AuthenticationContext/index.js";
3
3
  import { IdentityContext } from "../IdentityContext/index.js";
4
+ import { LoginTenantResolver } from "../LoginTenantResolver/index.js";
5
+ import { TenantContext } from "../../tenancy/abstractions.js";
6
+ import { LogInError } from "./LogInError.js";
4
7
  class LoginUseCaseImpl {
5
- constructor(authContext, identityContext, logInRepository){
8
+ constructor(authContext, identityContext, logInRepository, tenantResolver, tenantContext){
6
9
  this.authContext = authContext;
7
10
  this.identityContext = identityContext;
8
11
  this.logInRepository = logInRepository;
12
+ this.tenantResolver = tenantResolver;
13
+ this.tenantContext = tenantContext;
9
14
  this.validatePermissions = (permissions)=>{
10
15
  const appPermissions = permissions.filter((p)=>"aacl" !== p.name);
11
16
  if (0 === appPermissions.length) throw new Error("You have no permissions on this tenant!");
@@ -14,9 +19,52 @@ class LoginUseCaseImpl {
14
19
  async execute(params) {
15
20
  this.authContext.setIdTokenProvider(params.idTokenProvider);
16
21
  if (params.logoutCallback) this.authContext.setLogoutCallback(params.logoutCallback);
17
- const identity = await this.logInRepository.login();
22
+ let result;
23
+ try {
24
+ result = await this.logInRepository.login();
25
+ } catch (error) {
26
+ const logInError = LogInError.from(error);
27
+ return this.applyResolution(await this.tenantResolver.resolveError(logInError), ()=>{
28
+ throw logInError;
29
+ });
30
+ }
31
+ if ("selectTenant" === result.step) {
32
+ const resolution = await this.tenantResolver.resolveTenants(result.tenants);
33
+ return this.applyResolution(resolution, ()=>{
34
+ throw new LogInError({
35
+ code: "Security/Identity/TenantSelectionFailed",
36
+ message: "Unable to find a tenant to log into."
37
+ });
38
+ });
39
+ }
40
+ const { identity } = result;
18
41
  this.validatePermissions(identity.getPermissions());
19
42
  this.identityContext.setIdentity(identity);
43
+ return {
44
+ type: "loggedIn"
45
+ };
46
+ }
47
+ applyResolution(resolution, unresolved) {
48
+ switch(resolution.type){
49
+ case "switchTenant":
50
+ if (resolution.tenantId === this.tenantContext.getCurrentTenant()) return unresolved();
51
+ this.tenantContext.setTenant(resolution.tenantId);
52
+ return {
53
+ type: "switchingTenant",
54
+ tenantId: resolution.tenantId
55
+ };
56
+ case "selectTenant":
57
+ return {
58
+ type: "selectTenant",
59
+ tenants: resolution.tenants
60
+ };
61
+ case "noTenantAccess":
62
+ return {
63
+ type: "noTenantAccess"
64
+ };
65
+ default:
66
+ return unresolved();
67
+ }
20
68
  }
21
69
  }
22
70
  const LogInUseCase_LogInUseCase = LogInUseCase.createImplementation({
@@ -24,7 +72,9 @@ const LogInUseCase_LogInUseCase = LogInUseCase.createImplementation({
24
72
  dependencies: [
25
73
  AuthenticationContext,
26
74
  IdentityContext,
27
- LogInRepository
75
+ LogInRepository,
76
+ LoginTenantResolver,
77
+ TenantContext
28
78
  ]
29
79
  });
30
80
  export { LogInUseCase_LogInUseCase as LogInUseCase };
@@ -1 +1 @@
1
- {"version":3,"file":"features/security/LogIn/LogInUseCase.js","sources":["../../../../src/features/security/LogIn/LogInUseCase.ts"],"sourcesContent":["import { LogInRepository, LogInUseCase as UseCase } from \"./abstractions.js\";\nimport { AuthenticationContext } from \"~/features/security/AuthenticationContext/index.js\";\nimport { IdentityContext } from \"~/features/security/IdentityContext/index.js\";\nimport type { Identity } from \"~/domain/Identity.js\";\n\nclass LoginUseCaseImpl implements UseCase.Interface {\n constructor(\n private authContext: AuthenticationContext.Interface,\n private identityContext: IdentityContext.Interface,\n private logInRepository: LogInRepository.Interface\n ) {}\n\n async execute(params: UseCase.Params): Promise<void> {\n // 1. Set authentication providers\n this.authContext.setIdTokenProvider(params.idTokenProvider);\n if (params.logoutCallback) {\n this.authContext.setLogoutCallback(params.logoutCallback);\n }\n\n // TODO: if it's a multi-tenant system, and current tenant is not known (no URL param, no localStorage),\n // execute the GET_DEFAULT_TENANT query to first determine the tenant. Only then login the user.\n\n const identity = await this.logInRepository.login();\n\n // 3. Ensure identity has some permissions\n this.validatePermissions(identity.getPermissions());\n\n // 4. Set identity (user is now \"logged in\")\n this.identityContext.setIdentity(identity);\n }\n\n private validatePermissions = (permissions: Identity.Permission[]) => {\n const appPermissions = permissions.filter(p => p.name !== \"aacl\");\n if (appPermissions.length === 0) {\n throw new Error(\"You have no permissions on this tenant!\");\n }\n };\n}\n\nexport const LogInUseCase = UseCase.createImplementation({\n implementation: LoginUseCaseImpl,\n dependencies: [AuthenticationContext, IdentityContext, LogInRepository]\n});\n"],"names":["LoginUseCaseImpl","authContext","identityContext","logInRepository","permissions","appPermissions","p","Error","params","identity","LogInUseCase","UseCase","AuthenticationContext","IdentityContext","LogInRepository"],"mappings":";;;AAKA,MAAMA;IACF,YACYC,WAA4C,EAC5CC,eAA0C,EAC1CC,eAA0C,CACpD;aAHUF,WAAW,GAAXA;aACAC,eAAe,GAAfA;aACAC,eAAe,GAAfA;aAsBJ,mBAAmB,GAAG,CAACC;YAC3B,MAAMC,iBAAiBD,YAAY,MAAM,CAACE,CAAAA,IAAKA,AAAW,WAAXA,EAAE,IAAI;YACrD,IAAID,AAA0B,MAA1BA,eAAe,MAAM,EACrB,MAAM,IAAIE,MAAM;QAExB;IA1BG;IAEH,MAAM,QAAQC,MAAsB,EAAiB;QAEjD,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAACA,OAAO,eAAe;QAC1D,IAAIA,OAAO,cAAc,EACrB,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAACA,OAAO,cAAc;QAM5D,MAAMC,WAAW,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK;QAGjD,IAAI,CAAC,mBAAmB,CAACA,SAAS,cAAc;QAGhD,IAAI,CAAC,eAAe,CAAC,WAAW,CAACA;IACrC;AAQJ;AAEO,MAAMC,4BAAeC,aAAAA,oBAA4B,CAAC;IACrD,gBAAgBX;IAChB,cAAc;QAACY;QAAuBC;QAAiBC;KAAgB;AAC3E"}
1
+ {"version":3,"file":"features/security/LogIn/LogInUseCase.js","sources":["../../../../src/features/security/LogIn/LogInUseCase.ts"],"sourcesContent":["import { LogInRepository, LogInUseCase as UseCase } from \"./abstractions.js\";\nimport { AuthenticationContext } from \"~/features/security/AuthenticationContext/index.js\";\nimport { IdentityContext } from \"~/features/security/IdentityContext/index.js\";\nimport { LoginTenantResolver } from \"~/features/security/LoginTenantResolver/index.js\";\nimport { TenantContext } from \"~/features/tenancy/abstractions.js\";\nimport type { Identity } from \"~/domain/Identity.js\";\nimport { LogInError } from \"./LogInError.js\";\n\nclass LoginUseCaseImpl implements UseCase.Interface {\n constructor(\n private authContext: AuthenticationContext.Interface,\n private identityContext: IdentityContext.Interface,\n private logInRepository: LogInRepository.Interface,\n private tenantResolver: LoginTenantResolver.Interface,\n private tenantContext: TenantContext.Interface\n ) {}\n\n async execute(params: UseCase.Params): Promise<UseCase.Result> {\n // 1. Set authentication providers\n this.authContext.setIdTokenProvider(params.idTokenProvider);\n if (params.logoutCallback) {\n this.authContext.setLogoutCallback(params.logoutCallback);\n }\n\n // 2. Log in on the current tenant. If the user can't, find the tenant they can log into.\n let result: LogInRepository.Result;\n try {\n result = await this.logInRepository.login();\n } catch (error) {\n const logInError = LogInError.from(error);\n return this.applyResolution(await this.tenantResolver.resolveError(logInError), () => {\n throw logInError;\n });\n }\n\n if (result.step === \"selectTenant\") {\n const resolution = await this.tenantResolver.resolveTenants(result.tenants);\n return this.applyResolution(resolution, () => {\n throw new LogInError({\n code: \"Security/Identity/TenantSelectionFailed\",\n message: \"Unable to find a tenant to log into.\"\n });\n });\n }\n\n const { identity } = result;\n\n // 3. Ensure identity has some permissions\n this.validatePermissions(identity.getPermissions());\n\n // 4. Set identity (user is now \"logged in\")\n this.identityContext.setIdentity(identity);\n\n return { type: \"loggedIn\" };\n }\n\n private applyResolution(\n resolution: LoginTenantResolver.Resolution,\n unresolved: () => never\n ): UseCase.Result {\n switch (resolution.type) {\n case \"switchTenant\":\n // Setting the current tenant again changes nothing and would leave the login pending.\n if (resolution.tenantId === this.tenantContext.getCurrentTenant()) {\n return unresolved();\n }\n // Changing the tenant reloads the app, which logs in again on the new tenant.\n this.tenantContext.setTenant(resolution.tenantId);\n return { type: \"switchingTenant\", tenantId: resolution.tenantId };\n case \"selectTenant\":\n return { type: \"selectTenant\", tenants: resolution.tenants };\n case \"noTenantAccess\":\n return { type: \"noTenantAccess\" };\n default:\n return unresolved();\n }\n }\n\n private validatePermissions = (permissions: Identity.Permission[]) => {\n const appPermissions = permissions.filter(p => p.name !== \"aacl\");\n if (appPermissions.length === 0) {\n throw new Error(\"You have no permissions on this tenant!\");\n }\n };\n}\n\nexport const LogInUseCase = UseCase.createImplementation({\n implementation: LoginUseCaseImpl,\n dependencies: [\n AuthenticationContext,\n IdentityContext,\n LogInRepository,\n LoginTenantResolver,\n TenantContext\n ]\n});\n"],"names":["LoginUseCaseImpl","authContext","identityContext","logInRepository","tenantResolver","tenantContext","permissions","appPermissions","p","Error","params","result","error","logInError","LogInError","resolution","identity","unresolved","LogInUseCase","UseCase","AuthenticationContext","IdentityContext","LogInRepository","LoginTenantResolver","TenantContext"],"mappings":";;;;;;AAQA,MAAMA;IACF,YACYC,WAA4C,EAC5CC,eAA0C,EAC1CC,eAA0C,EAC1CC,cAA6C,EAC7CC,aAAsC,CAChD;aALUJ,WAAW,GAAXA;aACAC,eAAe,GAAfA;aACAC,eAAe,GAAfA;aACAC,cAAc,GAAdA;aACAC,aAAa,GAAbA;aAgEJ,mBAAmB,GAAG,CAACC;YAC3B,MAAMC,iBAAiBD,YAAY,MAAM,CAACE,CAAAA,IAAKA,AAAW,WAAXA,EAAE,IAAI;YACrD,IAAID,AAA0B,MAA1BA,eAAe,MAAM,EACrB,MAAM,IAAIE,MAAM;QAExB;IApEG;IAEH,MAAM,QAAQC,MAAsB,EAA2B;QAE3D,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAACA,OAAO,eAAe;QAC1D,IAAIA,OAAO,cAAc,EACrB,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAACA,OAAO,cAAc;QAI5D,IAAIC;QACJ,IAAI;YACAA,SAAS,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK;QAC7C,EAAE,OAAOC,OAAO;YACZ,MAAMC,aAAaC,WAAW,IAAI,CAACF;YACnC,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,CAACC,aAAa;gBAC5E,MAAMA;YACV;QACJ;QAEA,IAAIF,AAAgB,mBAAhBA,OAAO,IAAI,EAAqB;YAChC,MAAMI,aAAa,MAAM,IAAI,CAAC,cAAc,CAAC,cAAc,CAACJ,OAAO,OAAO;YAC1E,OAAO,IAAI,CAAC,eAAe,CAACI,YAAY;gBACpC,MAAM,IAAID,WAAW;oBACjB,MAAM;oBACN,SAAS;gBACb;YACJ;QACJ;QAEA,MAAM,EAAEE,QAAQ,EAAE,GAAGL;QAGrB,IAAI,CAAC,mBAAmB,CAACK,SAAS,cAAc;QAGhD,IAAI,CAAC,eAAe,CAAC,WAAW,CAACA;QAEjC,OAAO;YAAE,MAAM;QAAW;IAC9B;IAEQ,gBACJD,UAA0C,EAC1CE,UAAuB,EACT;QACd,OAAQF,WAAW,IAAI;YACnB,KAAK;gBAED,IAAIA,WAAW,QAAQ,KAAK,IAAI,CAAC,aAAa,CAAC,gBAAgB,IAC3D,OAAOE;gBAGX,IAAI,CAAC,aAAa,CAAC,SAAS,CAACF,WAAW,QAAQ;gBAChD,OAAO;oBAAE,MAAM;oBAAmB,UAAUA,WAAW,QAAQ;gBAAC;YACpE,KAAK;gBACD,OAAO;oBAAE,MAAM;oBAAgB,SAASA,WAAW,OAAO;gBAAC;YAC/D,KAAK;gBACD,OAAO;oBAAE,MAAM;gBAAiB;YACpC;gBACI,OAAOE;QACf;IACJ;AAQJ;AAEO,MAAMC,4BAAeC,aAAAA,oBAA4B,CAAC;IACrD,gBAAgBnB;IAChB,cAAc;QACVoB;QACAC;QACAC;QACAC;QACAC;KACH;AACL"}
@@ -0,0 +1 @@
1
+ export {};