@gzl10/nexus-sdk 0.12.7 → 0.13.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.
- package/dist/chunk-6WVPKBUS.js +364 -0
- package/dist/chunk-JIEVWBDA.js +256 -0
- package/dist/chunk-WZM5DHUI.js +377 -0
- package/dist/collection/index.d.ts +506 -0
- package/dist/collection/index.js +1501 -0
- package/dist/entity-kPgsCMdR.d.ts +1504 -0
- package/dist/field-CngHXora.d.ts +375 -0
- package/dist/fields/index.d.ts +1305 -0
- package/dist/fields/index.js +58 -0
- package/dist/index.d.ts +387 -2302
- package/dist/index.js +25 -69
- package/dist/masters/index.d.ts +120 -0
- package/dist/masters/index.js +689 -0
- package/package.json +23 -2
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
// src/fields/id.ts
|
|
2
|
+
var ID_TYPE_CONFIG = {
|
|
3
|
+
ulid: { size: 26, input: "text" },
|
|
4
|
+
uuid: { size: 36, input: "uuid" },
|
|
5
|
+
nanoid: { size: 21, input: "text" },
|
|
6
|
+
cuid2: { size: 24, input: "text" }
|
|
7
|
+
};
|
|
8
|
+
function calculatePatternSize(pattern, prefix, suffix) {
|
|
9
|
+
let size = 0;
|
|
10
|
+
const tokens = pattern.match(/\{[^}]+\}/g) || [];
|
|
11
|
+
for (const token of tokens) {
|
|
12
|
+
const tokenName = token.slice(1, -1);
|
|
13
|
+
if (tokenName === "prefix") {
|
|
14
|
+
size += prefix?.length ?? 0;
|
|
15
|
+
} else if (tokenName === "suffix") {
|
|
16
|
+
size += suffix?.length ?? 0;
|
|
17
|
+
} else if (tokenName === "year") {
|
|
18
|
+
size += 4;
|
|
19
|
+
} else if (tokenName === "year2") {
|
|
20
|
+
size += 2;
|
|
21
|
+
} else if (tokenName === "month" || tokenName === "day") {
|
|
22
|
+
size += 2;
|
|
23
|
+
} else if (tokenName.startsWith("seq:")) {
|
|
24
|
+
const paddingStr = tokenName.split(":")[1] ?? "6";
|
|
25
|
+
const padding = parseInt(paddingStr, 10) || 6;
|
|
26
|
+
size += padding;
|
|
27
|
+
} else if (tokenName === "seq") {
|
|
28
|
+
size += 10;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const literals = pattern.replace(/\{[^}]+\}/g, "");
|
|
32
|
+
size += literals.length;
|
|
33
|
+
return Math.max(size + 10, 50);
|
|
34
|
+
}
|
|
35
|
+
function useIdField(type = "ulid", config) {
|
|
36
|
+
const label = config?.label ?? "ID";
|
|
37
|
+
if (type === "auto") {
|
|
38
|
+
return {
|
|
39
|
+
label,
|
|
40
|
+
input: "number",
|
|
41
|
+
hidden: true,
|
|
42
|
+
db: { type: "integer", nullable: false, primary: true, autoIncrement: true, idType: "auto" }
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (type === "custom") {
|
|
46
|
+
return {
|
|
47
|
+
label,
|
|
48
|
+
input: "text",
|
|
49
|
+
hidden: true,
|
|
50
|
+
db: { type: "string", size: config?.size ?? 50, nullable: false, primary: true, idType: "custom" }
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (type === "pattern") {
|
|
54
|
+
const patternConfig = config?.pattern;
|
|
55
|
+
if (!patternConfig || !patternConfig.pattern) {
|
|
56
|
+
throw new Error("Pattern configuration is required for pattern ID type");
|
|
57
|
+
}
|
|
58
|
+
const size2 = calculatePatternSize(patternConfig.pattern, patternConfig.prefix, patternConfig.suffix);
|
|
59
|
+
return {
|
|
60
|
+
label,
|
|
61
|
+
input: "text",
|
|
62
|
+
hidden: true,
|
|
63
|
+
db: {
|
|
64
|
+
type: "string",
|
|
65
|
+
size: size2,
|
|
66
|
+
nullable: false,
|
|
67
|
+
primary: true,
|
|
68
|
+
idType: "pattern",
|
|
69
|
+
patternConfig
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const { size, input } = ID_TYPE_CONFIG[type];
|
|
74
|
+
return {
|
|
75
|
+
label,
|
|
76
|
+
input,
|
|
77
|
+
hidden: true,
|
|
78
|
+
db: { type: "string", size, nullable: false, primary: true, idType: type }
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/fields/text.ts
|
|
83
|
+
function useTextField(config) {
|
|
84
|
+
const {
|
|
85
|
+
label,
|
|
86
|
+
hint,
|
|
87
|
+
required,
|
|
88
|
+
size = 255,
|
|
89
|
+
minLength,
|
|
90
|
+
nullable,
|
|
91
|
+
index,
|
|
92
|
+
unique,
|
|
93
|
+
defaultValue,
|
|
94
|
+
placeholder,
|
|
95
|
+
validation,
|
|
96
|
+
meta,
|
|
97
|
+
...overrides
|
|
98
|
+
} = config;
|
|
99
|
+
const min = minLength ?? (required ? 1 : 0);
|
|
100
|
+
return {
|
|
101
|
+
label,
|
|
102
|
+
input: "text",
|
|
103
|
+
hint,
|
|
104
|
+
required: required ?? false,
|
|
105
|
+
defaultValue,
|
|
106
|
+
placeholder,
|
|
107
|
+
db: {
|
|
108
|
+
type: "string",
|
|
109
|
+
size,
|
|
110
|
+
nullable: nullable ?? !required,
|
|
111
|
+
default: defaultValue,
|
|
112
|
+
index: index ?? false,
|
|
113
|
+
unique: unique ?? false
|
|
114
|
+
},
|
|
115
|
+
validation: {
|
|
116
|
+
min,
|
|
117
|
+
max: size,
|
|
118
|
+
...validation
|
|
119
|
+
// Custom validation overrides/extends
|
|
120
|
+
},
|
|
121
|
+
meta: {
|
|
122
|
+
searchable: true,
|
|
123
|
+
...meta
|
|
124
|
+
},
|
|
125
|
+
...overrides
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function useTextUniqueField(config) {
|
|
129
|
+
return useTextField({
|
|
130
|
+
...config,
|
|
131
|
+
required: true,
|
|
132
|
+
unique: true,
|
|
133
|
+
index: true
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/fields/url.ts
|
|
138
|
+
function useUrlField(config) {
|
|
139
|
+
const {
|
|
140
|
+
label,
|
|
141
|
+
hint,
|
|
142
|
+
required,
|
|
143
|
+
size = 500,
|
|
144
|
+
nullable,
|
|
145
|
+
index,
|
|
146
|
+
defaultValue,
|
|
147
|
+
placeholder,
|
|
148
|
+
meta,
|
|
149
|
+
...overrides
|
|
150
|
+
} = config;
|
|
151
|
+
return {
|
|
152
|
+
label,
|
|
153
|
+
input: "url",
|
|
154
|
+
hint,
|
|
155
|
+
required: required ?? false,
|
|
156
|
+
defaultValue,
|
|
157
|
+
placeholder,
|
|
158
|
+
db: {
|
|
159
|
+
type: "string",
|
|
160
|
+
size,
|
|
161
|
+
nullable: nullable ?? !required,
|
|
162
|
+
default: defaultValue,
|
|
163
|
+
index: index ?? false
|
|
164
|
+
},
|
|
165
|
+
validation: {
|
|
166
|
+
max: size
|
|
167
|
+
},
|
|
168
|
+
meta: {
|
|
169
|
+
searchable: false,
|
|
170
|
+
...meta
|
|
171
|
+
},
|
|
172
|
+
...overrides
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/fields/select.ts
|
|
177
|
+
function createStaticSelect(config) {
|
|
178
|
+
const { label, options, defaultValue, hint, required, nullable, size, index, meta, ...overrides } = config;
|
|
179
|
+
const normalizedOptions = options.map(
|
|
180
|
+
(opt) => typeof opt === "string" ? { value: opt, label: opt } : opt
|
|
181
|
+
);
|
|
182
|
+
return {
|
|
183
|
+
label,
|
|
184
|
+
input: "select",
|
|
185
|
+
hint,
|
|
186
|
+
required: required ?? false,
|
|
187
|
+
defaultValue,
|
|
188
|
+
db: {
|
|
189
|
+
type: "string",
|
|
190
|
+
size: size ?? 50,
|
|
191
|
+
nullable: nullable ?? !required,
|
|
192
|
+
default: defaultValue,
|
|
193
|
+
index: index ?? false
|
|
194
|
+
},
|
|
195
|
+
options: {
|
|
196
|
+
static: normalizedOptions
|
|
197
|
+
},
|
|
198
|
+
meta: {
|
|
199
|
+
searchable: true,
|
|
200
|
+
...meta
|
|
201
|
+
},
|
|
202
|
+
...overrides
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function createRelationSelect(config) {
|
|
206
|
+
const {
|
|
207
|
+
label,
|
|
208
|
+
table,
|
|
209
|
+
column = "id",
|
|
210
|
+
endpoint,
|
|
211
|
+
valueField = "id",
|
|
212
|
+
labelField = "name",
|
|
213
|
+
onDelete = "SET NULL",
|
|
214
|
+
hint,
|
|
215
|
+
required,
|
|
216
|
+
nullable,
|
|
217
|
+
size = 26,
|
|
218
|
+
index,
|
|
219
|
+
meta,
|
|
220
|
+
...overrides
|
|
221
|
+
} = config;
|
|
222
|
+
return {
|
|
223
|
+
label,
|
|
224
|
+
input: "select",
|
|
225
|
+
hint,
|
|
226
|
+
required: required ?? false,
|
|
227
|
+
db: {
|
|
228
|
+
type: "string",
|
|
229
|
+
size,
|
|
230
|
+
nullable: nullable ?? !required,
|
|
231
|
+
index: index ?? true
|
|
232
|
+
},
|
|
233
|
+
relation: {
|
|
234
|
+
table,
|
|
235
|
+
column,
|
|
236
|
+
onDelete
|
|
237
|
+
},
|
|
238
|
+
options: {
|
|
239
|
+
endpoint,
|
|
240
|
+
valueField,
|
|
241
|
+
labelField
|
|
242
|
+
},
|
|
243
|
+
meta: {
|
|
244
|
+
searchable: true,
|
|
245
|
+
...meta
|
|
246
|
+
},
|
|
247
|
+
...overrides
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function useSelectField(config) {
|
|
251
|
+
if ("table" in config) {
|
|
252
|
+
return createRelationSelect(config);
|
|
253
|
+
}
|
|
254
|
+
return createStaticSelect(config);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/fields/number.ts
|
|
258
|
+
function useNumberField(config) {
|
|
259
|
+
const { label, hint, required, defaultValue, nullable, validation, meta, ...overrides } = config;
|
|
260
|
+
return {
|
|
261
|
+
label,
|
|
262
|
+
input: "number",
|
|
263
|
+
hint,
|
|
264
|
+
required: required ?? false,
|
|
265
|
+
defaultValue,
|
|
266
|
+
db: {
|
|
267
|
+
type: "integer",
|
|
268
|
+
nullable: nullable ?? !required,
|
|
269
|
+
...defaultValue !== void 0 && { default: defaultValue }
|
|
270
|
+
},
|
|
271
|
+
validation,
|
|
272
|
+
meta: {
|
|
273
|
+
sortable: true,
|
|
274
|
+
...meta
|
|
275
|
+
},
|
|
276
|
+
...overrides
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/fields/localized.ts
|
|
281
|
+
function useLocalizedField(config) {
|
|
282
|
+
const { label, hint, required = true, meta, ...overrides } = config;
|
|
283
|
+
return {
|
|
284
|
+
label,
|
|
285
|
+
input: "text",
|
|
286
|
+
hint,
|
|
287
|
+
required,
|
|
288
|
+
db: {
|
|
289
|
+
type: "json",
|
|
290
|
+
nullable: !required
|
|
291
|
+
},
|
|
292
|
+
meta: {
|
|
293
|
+
searchable: true,
|
|
294
|
+
sortable: true,
|
|
295
|
+
...meta
|
|
296
|
+
},
|
|
297
|
+
...overrides
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/fields/icon.ts
|
|
302
|
+
function useIconField(config) {
|
|
303
|
+
const { label, hint, required, size = 100, placeholder, meta, ...overrides } = config;
|
|
304
|
+
return {
|
|
305
|
+
label,
|
|
306
|
+
input: "icon",
|
|
307
|
+
hint,
|
|
308
|
+
required: required ?? false,
|
|
309
|
+
placeholder,
|
|
310
|
+
db: {
|
|
311
|
+
type: "string",
|
|
312
|
+
size,
|
|
313
|
+
nullable: !required
|
|
314
|
+
},
|
|
315
|
+
meta: {
|
|
316
|
+
...meta
|
|
317
|
+
},
|
|
318
|
+
...overrides
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/fields/common.ts
|
|
323
|
+
var idField = useIdField("ulid");
|
|
324
|
+
var userIdField = {
|
|
325
|
+
label: { en: "User", es: "Usuario" },
|
|
326
|
+
input: "select",
|
|
327
|
+
required: true,
|
|
328
|
+
db: { type: "string", size: 26, nullable: false, index: true },
|
|
329
|
+
relation: {
|
|
330
|
+
table: "users",
|
|
331
|
+
column: "id",
|
|
332
|
+
onDelete: "CASCADE"
|
|
333
|
+
},
|
|
334
|
+
meta: { showInForm: false }
|
|
335
|
+
};
|
|
336
|
+
var isActiveField = {
|
|
337
|
+
label: { en: "Active", es: "Activo" },
|
|
338
|
+
input: "switch",
|
|
339
|
+
defaultValue: true,
|
|
340
|
+
db: { type: "boolean", nullable: false, default: true },
|
|
341
|
+
meta: { sortable: true }
|
|
342
|
+
};
|
|
343
|
+
var orderField = {
|
|
344
|
+
label: { en: "Order", es: "Orden" },
|
|
345
|
+
input: "number",
|
|
346
|
+
defaultValue: 0,
|
|
347
|
+
db: { type: "integer", nullable: false, default: 0 },
|
|
348
|
+
meta: { sortable: true }
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
export {
|
|
352
|
+
useIdField,
|
|
353
|
+
useTextField,
|
|
354
|
+
useTextUniqueField,
|
|
355
|
+
useUrlField,
|
|
356
|
+
useSelectField,
|
|
357
|
+
useNumberField,
|
|
358
|
+
useLocalizedField,
|
|
359
|
+
useIconField,
|
|
360
|
+
idField,
|
|
361
|
+
userIdField,
|
|
362
|
+
isActiveField,
|
|
363
|
+
orderField
|
|
364
|
+
};
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// src/types/localization.ts
|
|
2
|
+
function resolveLocalized(value, locale) {
|
|
3
|
+
if (!value) return "";
|
|
4
|
+
if (typeof value === "string") return value;
|
|
5
|
+
return value[locale] || value["en"] || Object.values(value)[0] || "";
|
|
6
|
+
}
|
|
7
|
+
function getCountLabel(label, labelPlural, count, locale) {
|
|
8
|
+
const resolved = count === 1 ? label : labelPlural || label;
|
|
9
|
+
return resolveLocalized(resolved, locale);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/types/field.ts
|
|
13
|
+
function refOptions(module, entityOrLabel, labelField) {
|
|
14
|
+
const hasEntity = labelField !== void 0;
|
|
15
|
+
return {
|
|
16
|
+
endpoint: hasEntity ? `/${module}/${entityOrLabel}` : `/${module}`,
|
|
17
|
+
valueField: "id",
|
|
18
|
+
labelField: hasEntity ? labelField : entityOrLabel ?? "name"
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function refMaster(master, options = {}) {
|
|
22
|
+
const table = `mst_${master}`;
|
|
23
|
+
return {
|
|
24
|
+
label: options.label ?? master,
|
|
25
|
+
input: options.input ?? "select",
|
|
26
|
+
required: options.required,
|
|
27
|
+
defaultValue: options.default,
|
|
28
|
+
db: { type: "string", size: 36 },
|
|
29
|
+
relation: { table, column: "id" },
|
|
30
|
+
options: {
|
|
31
|
+
endpoint: `/masters/${master}`,
|
|
32
|
+
valueField: "id",
|
|
33
|
+
labelField: "name"
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/types/manifest.ts
|
|
39
|
+
var CATEGORIES = {
|
|
40
|
+
data: { label: { en: "Data", es: "Datos" }, icon: "mdi:database", order: 1 },
|
|
41
|
+
content: { label: { en: "Content", es: "Contenido" }, icon: "mdi:file-document-outline", order: 2 },
|
|
42
|
+
assets: { label: { en: "Assets", es: "Recursos" }, icon: "mdi:folder-outline", order: 3 },
|
|
43
|
+
messaging: { label: { en: "Messaging", es: "Mensajer\xEDa" }, icon: "mdi:message-outline", order: 4 },
|
|
44
|
+
jobs: { label: { en: "Jobs", es: "Tareas" }, icon: "mdi:clock-outline", order: 5 },
|
|
45
|
+
ai: { label: { en: "AI", es: "IA" }, icon: "mdi:robot-outline", order: 6 },
|
|
46
|
+
analytics: { label: { en: "Analytics", es: "Anal\xEDticas" }, icon: "mdi:chart-line", order: 7 },
|
|
47
|
+
integrations: { label: { en: "Integrations", es: "Integraciones" }, icon: "mdi:puzzle-outline", order: 8 },
|
|
48
|
+
commerce: { label: { en: "Commerce", es: "Comercio" }, icon: "mdi:shopping-outline", order: 9 },
|
|
49
|
+
security: { label: { en: "Security", es: "Seguridad" }, icon: "mdi:shield-lock-outline", order: 10 },
|
|
50
|
+
legal: { label: { en: "Legal", es: "Legal" }, icon: "mdi:scale-balance", order: 11 },
|
|
51
|
+
settings: { label: { en: "Settings", es: "Configuraci\xF3n" }, icon: "mdi:cog-outline", order: 12 }
|
|
52
|
+
};
|
|
53
|
+
var CATEGORY_ORDER = Object.keys(CATEGORIES).sort((a, b) => CATEGORIES[a].order - CATEGORIES[b].order);
|
|
54
|
+
|
|
55
|
+
// src/generators.ts
|
|
56
|
+
function isPersistentEntity(entity) {
|
|
57
|
+
const withoutOwnTable = ["action", "external", "virtual", "computed", "single", "view"];
|
|
58
|
+
return !withoutOwnTable.includes(entity.type ?? "collection");
|
|
59
|
+
}
|
|
60
|
+
function isSingletonEntity(entity) {
|
|
61
|
+
return entity.type === "single";
|
|
62
|
+
}
|
|
63
|
+
function hasTable(entity) {
|
|
64
|
+
return "table" in entity && typeof entity.table === "string";
|
|
65
|
+
}
|
|
66
|
+
function getEntityName(entity) {
|
|
67
|
+
if ("table" in entity) {
|
|
68
|
+
const withoutPrefix = entity.table.replace(/^[a-z]{2,4}_/, "");
|
|
69
|
+
return toPascalCase(toSingular(withoutPrefix));
|
|
70
|
+
} else if ("key" in entity) {
|
|
71
|
+
return toPascalCase(entity.key);
|
|
72
|
+
} else {
|
|
73
|
+
const label = resolveLocalized(entity.label, "en");
|
|
74
|
+
return toSingular(label).replace(/\s+/g, "");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function getEntitySubject(entity) {
|
|
78
|
+
return entity.casl?.subject ?? tableToSubject(entity.table);
|
|
79
|
+
}
|
|
80
|
+
function toPascalCase(str) {
|
|
81
|
+
return str.split("_").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
|
|
82
|
+
}
|
|
83
|
+
function toSingular(str) {
|
|
84
|
+
if (str.endsWith("ies")) return str.slice(0, -3) + "y";
|
|
85
|
+
if (str.endsWith("s")) return str.slice(0, -1);
|
|
86
|
+
return str;
|
|
87
|
+
}
|
|
88
|
+
function tableToSubject(table) {
|
|
89
|
+
const match = table.match(/^([a-z]{2,4})_(.+)$/);
|
|
90
|
+
if (!match) {
|
|
91
|
+
const withoutPrefix = table.replace(/^[a-z]{2,4}_/, "");
|
|
92
|
+
return toPascalCase(toSingular(withoutPrefix));
|
|
93
|
+
}
|
|
94
|
+
const [, prefix, rest] = match;
|
|
95
|
+
const prefixPascal = prefix.charAt(0).toUpperCase() + prefix.slice(1);
|
|
96
|
+
return prefixPascal + toPascalCase(toSingular(rest));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/oidc/oidc-client.ts
|
|
100
|
+
import { ofetch } from "ofetch";
|
|
101
|
+
import * as jose from "jose";
|
|
102
|
+
function createOidcClient() {
|
|
103
|
+
const jwksCache = /* @__PURE__ */ new Map();
|
|
104
|
+
async function discover(issuerUrl) {
|
|
105
|
+
const url = `${issuerUrl.replace(/\/$/, "")}/.well-known/openid-configuration`;
|
|
106
|
+
return ofetch(url);
|
|
107
|
+
}
|
|
108
|
+
function buildAuthorizationUrl(authorizationEndpoint, params) {
|
|
109
|
+
const url = new URL(authorizationEndpoint);
|
|
110
|
+
url.searchParams.set("client_id", params.clientId);
|
|
111
|
+
url.searchParams.set("redirect_uri", params.redirectUri);
|
|
112
|
+
url.searchParams.set("response_type", params.responseType || "code");
|
|
113
|
+
url.searchParams.set("scope", params.scopes.join(" "));
|
|
114
|
+
url.searchParams.set("state", params.state);
|
|
115
|
+
url.searchParams.set("nonce", params.nonce);
|
|
116
|
+
if (params.hostedDomain) {
|
|
117
|
+
url.searchParams.set("hd", params.hostedDomain);
|
|
118
|
+
}
|
|
119
|
+
return url.toString();
|
|
120
|
+
}
|
|
121
|
+
async function exchangeCode(params) {
|
|
122
|
+
const body = new URLSearchParams({
|
|
123
|
+
grant_type: "authorization_code",
|
|
124
|
+
client_id: params.clientId,
|
|
125
|
+
client_secret: params.clientSecret,
|
|
126
|
+
code: params.code,
|
|
127
|
+
redirect_uri: params.redirectUri
|
|
128
|
+
});
|
|
129
|
+
return ofetch(params.tokenEndpoint, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
headers: {
|
|
132
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
133
|
+
},
|
|
134
|
+
body: body.toString()
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async function getUserInfo(accessToken, userInfoEndpoint) {
|
|
138
|
+
return ofetch(userInfoEndpoint, {
|
|
139
|
+
headers: {
|
|
140
|
+
Authorization: `Bearer ${accessToken}`
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function getJwksVerifier(jwksUri) {
|
|
145
|
+
let verifier = jwksCache.get(jwksUri);
|
|
146
|
+
if (!verifier) {
|
|
147
|
+
verifier = jose.createRemoteJWKSet(new URL(jwksUri));
|
|
148
|
+
jwksCache.set(jwksUri, verifier);
|
|
149
|
+
}
|
|
150
|
+
return verifier;
|
|
151
|
+
}
|
|
152
|
+
async function validateIdToken(idToken, config) {
|
|
153
|
+
const jwks = getJwksVerifier(config.jwksUri);
|
|
154
|
+
const { payload } = await jose.jwtVerify(idToken, jwks, {
|
|
155
|
+
issuer: config.issuer,
|
|
156
|
+
audience: config.clientId
|
|
157
|
+
});
|
|
158
|
+
if (config.nonce && payload["nonce"] !== config.nonce) {
|
|
159
|
+
throw new Error("Invalid nonce in ID token");
|
|
160
|
+
}
|
|
161
|
+
return payload;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
discover,
|
|
165
|
+
buildAuthorizationUrl,
|
|
166
|
+
exchangeCode,
|
|
167
|
+
getUserInfo,
|
|
168
|
+
validateIdToken
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
var _client = null;
|
|
172
|
+
function getOidcClient() {
|
|
173
|
+
if (!_client) {
|
|
174
|
+
_client = createOidcClient();
|
|
175
|
+
}
|
|
176
|
+
return _client;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/oidc/state-manager.ts
|
|
180
|
+
var DEFAULT_TTL_MS = 10 * 60 * 1e3;
|
|
181
|
+
function createStateManager(ttlMs = DEFAULT_TTL_MS) {
|
|
182
|
+
const stateStore = /* @__PURE__ */ new Map();
|
|
183
|
+
function store(state, data) {
|
|
184
|
+
stateStore.set(state, data);
|
|
185
|
+
}
|
|
186
|
+
function verify(state) {
|
|
187
|
+
const stateData = stateStore.get(state);
|
|
188
|
+
if (!stateData) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
if (Date.now() - stateData.createdAt > ttlMs) {
|
|
192
|
+
stateStore.delete(state);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return stateData;
|
|
196
|
+
}
|
|
197
|
+
function clear(state) {
|
|
198
|
+
stateStore.delete(state);
|
|
199
|
+
}
|
|
200
|
+
function getTtl() {
|
|
201
|
+
return ttlMs;
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
store,
|
|
205
|
+
verify,
|
|
206
|
+
clear,
|
|
207
|
+
getTtl
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/oidc/domain-filter.ts
|
|
212
|
+
function checkAllowedDomain(email, allowedDomainsJson) {
|
|
213
|
+
const emailDomain = email.split("@")[1]?.toLowerCase();
|
|
214
|
+
if (!emailDomain) {
|
|
215
|
+
return { allowed: false, domain: "" };
|
|
216
|
+
}
|
|
217
|
+
if (!allowedDomainsJson) {
|
|
218
|
+
return { allowed: true, domain: emailDomain };
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const allowedDomains = JSON.parse(allowedDomainsJson);
|
|
222
|
+
if (!Array.isArray(allowedDomains) || allowedDomains.length === 0) {
|
|
223
|
+
return { allowed: true, domain: emailDomain };
|
|
224
|
+
}
|
|
225
|
+
const normalizedDomains = allowedDomains.map((d) => d.toLowerCase());
|
|
226
|
+
const allowed = normalizedDomains.includes(emailDomain);
|
|
227
|
+
return { allowed, domain: emailDomain };
|
|
228
|
+
} catch {
|
|
229
|
+
return { allowed: true, domain: emailDomain };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function assertAllowedDomain(email, allowedDomainsJson, errorClass = Error) {
|
|
233
|
+
const result = checkAllowedDomain(email, allowedDomainsJson);
|
|
234
|
+
if (!result.allowed) {
|
|
235
|
+
throw new errorClass(`Email domain '${result.domain}' is not allowed`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export {
|
|
240
|
+
resolveLocalized,
|
|
241
|
+
getCountLabel,
|
|
242
|
+
refOptions,
|
|
243
|
+
refMaster,
|
|
244
|
+
CATEGORIES,
|
|
245
|
+
CATEGORY_ORDER,
|
|
246
|
+
isPersistentEntity,
|
|
247
|
+
isSingletonEntity,
|
|
248
|
+
hasTable,
|
|
249
|
+
getEntityName,
|
|
250
|
+
getEntitySubject,
|
|
251
|
+
createOidcClient,
|
|
252
|
+
getOidcClient,
|
|
253
|
+
createStateManager,
|
|
254
|
+
checkAllowedDomain,
|
|
255
|
+
assertAllowedDomain
|
|
256
|
+
};
|