@carlonicora/nextjs-jsonapi 1.8.0 → 1.9.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/{BlockNoteEditor-N534QVBR.mjs → BlockNoteEditor-MXNV2O43.mjs} +5 -5
- package/dist/{BlockNoteEditor-KSPPX6JO.js → BlockNoteEditor-O32YPSNN.js} +15 -15
- package/dist/{BlockNoteEditor-KSPPX6JO.js.map → BlockNoteEditor-O32YPSNN.js.map} +1 -1
- package/dist/{JsonApiRequest-SYZ6FGCA.mjs → JsonApiRequest-ARXYJ2AG.mjs} +2 -2
- package/dist/JsonApiRequest-FZAFAES3.js +24 -0
- package/dist/{JsonApiRequest-2OM5NDAW.js.map → JsonApiRequest-FZAFAES3.js.map} +1 -1
- package/dist/{chunk-IGOWVLJH.mjs → chunk-33UYFFIK.mjs} +5 -1
- package/dist/chunk-33UYFFIK.mjs.map +1 -0
- package/dist/{chunk-SAL7XW2G.mjs → chunk-CHNXYTKO.mjs} +40 -1
- package/dist/chunk-CHNXYTKO.mjs.map +1 -0
- package/dist/{chunk-AGWQ75PQ.js → chunk-I7OQ5WKX.js} +5 -1
- package/dist/chunk-I7OQ5WKX.js.map +1 -0
- package/dist/{chunk-7Z7FEMEB.js → chunk-LPBABDEP.js} +43 -23
- package/dist/chunk-LPBABDEP.js.map +1 -0
- package/dist/{chunk-CK5KLBZV.mjs → chunk-MOVONDSX.mjs} +28 -8
- package/dist/chunk-MOVONDSX.mjs.map +1 -0
- package/dist/{chunk-B426TLJC.js → chunk-PVN4DI3H.js} +395 -395
- package/dist/{chunk-B426TLJC.js.map → chunk-PVN4DI3H.js.map} +1 -1
- package/dist/{chunk-CKS6SVUK.js → chunk-UVXEP2ED.js} +41 -2
- package/dist/chunk-UVXEP2ED.js.map +1 -0
- package/dist/{chunk-TLBZWOCU.mjs → chunk-YHUK7PFT.mjs} +7 -7
- package/dist/chunk-YHUK7PFT.mjs.map +1 -0
- package/dist/client/index.js +5 -5
- package/dist/client/index.mjs +4 -4
- package/dist/components/index.js +5 -5
- package/dist/components/index.mjs +4 -4
- package/dist/contexts/index.js +5 -5
- package/dist/contexts/index.mjs +4 -4
- package/dist/core/index.d.mts +35 -1
- package/dist/core/index.d.ts +35 -1
- package/dist/core/index.js +13 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +13 -3
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +14 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14 -4
- package/dist/scripts/generate-web-module/templates/components/editor.template.js +19 -17
- package/dist/scripts/generate-web-module/templates/components/editor.template.js.map +1 -1
- package/dist/scripts/generate-web-module/transformers/i18n-generator.js +2 -2
- package/dist/scripts/generate-web-module/transformers/i18n-generator.js.map +1 -1
- package/dist/scripts/generate-web-module/utils/i18n-updater.d.ts.map +1 -1
- package/dist/scripts/generate-web-module/utils/i18n-updater.js +36 -14
- package/dist/scripts/generate-web-module/utils/i18n-updater.js.map +1 -1
- package/dist/server/index.js +12 -12
- package/dist/server/index.mjs +2 -2
- package/package.json +1 -1
- package/scripts/generate-web-module/templates/components/editor.template.ts +19 -17
- package/scripts/generate-web-module/transformers/i18n-generator.ts +2 -2
- package/scripts/generate-web-module/utils/i18n-updater.ts +42 -16
- package/src/client/JsonApiClient.ts +2 -0
- package/src/client/config.ts +3 -1
- package/src/components/containers/PageContainer.tsx +1 -1
- package/src/core/registry/ModuleRegistry.ts +33 -3
- package/src/core/registry/bootstrapStore.ts +63 -0
- package/src/core/registry/index.ts +1 -0
- package/src/unified/JsonApiRequest.ts +3 -1
- package/dist/JsonApiRequest-2OM5NDAW.js +0 -24
- package/dist/chunk-7Z7FEMEB.js.map +0 -1
- package/dist/chunk-AGWQ75PQ.js.map +0 -1
- package/dist/chunk-CK5KLBZV.mjs.map +0 -1
- package/dist/chunk-CKS6SVUK.js.map +0 -1
- package/dist/chunk-IGOWVLJH.mjs.map +0 -1
- package/dist/chunk-SAL7XW2G.mjs.map +0 -1
- package/dist/chunk-TLBZWOCU.mjs.map +0 -1
- /package/dist/{BlockNoteEditor-N534QVBR.mjs.map → BlockNoteEditor-MXNV2O43.mjs.map} +0 -0
- /package/dist/{JsonApiRequest-SYZ6FGCA.mjs.map → JsonApiRequest-ARXYJ2AG.mjs.map} +0 -0
|
@@ -271,7 +271,7 @@ function generateFormSchema(data: FrontendTemplateData): string {
|
|
|
271
271
|
// Add name field for Content-extending modules
|
|
272
272
|
if (extendsContent) {
|
|
273
273
|
schemaFields.push(` name: z.string().min(1, {
|
|
274
|
-
message: t(\`features.${names.camelCase}.fields.name.error\`),
|
|
274
|
+
message: t(\`features.${names.camelCase.toLowerCase()}.fields.name.error\`),
|
|
275
275
|
}),`);
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -283,7 +283,7 @@ function generateFormSchema(data: FrontendTemplateData): string {
|
|
|
283
283
|
schemaFields.push(` ${field.name}: z.string().optional(),`);
|
|
284
284
|
} else {
|
|
285
285
|
schemaFields.push(` ${field.name}: z.string().min(1, {
|
|
286
|
-
message: t(\`features.${names.camelCase}.fields.${field.name}.error\`),
|
|
286
|
+
message: t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.error\`),
|
|
287
287
|
}),`);
|
|
288
288
|
}
|
|
289
289
|
} else {
|
|
@@ -294,6 +294,7 @@ function generateFormSchema(data: FrontendTemplateData): string {
|
|
|
294
294
|
// Relationship fields
|
|
295
295
|
relationships.forEach((rel) => {
|
|
296
296
|
const fieldId = toCamelCase(rel.variant || rel.name);
|
|
297
|
+
const fieldIdLower = fieldId.toLowerCase();
|
|
297
298
|
if (rel.variant === AUTHOR_VARIANT) {
|
|
298
299
|
schemaFields.push(` ${fieldId}: userObjectSchema.refine((data) => data.id && data.id.length > 0, {
|
|
299
300
|
message: t(\`generic.relationships.author.error\`),
|
|
@@ -303,7 +304,7 @@ function generateFormSchema(data: FrontendTemplateData): string {
|
|
|
303
304
|
schemaFields.push(` ${fieldId}: entityObjectSchema.optional(),`);
|
|
304
305
|
} else {
|
|
305
306
|
schemaFields.push(` ${fieldId}: entityObjectSchema.refine((data) => data.id && data.id.length > 0, {
|
|
306
|
-
message: t(\`features.${names.camelCase}.relationships.${
|
|
307
|
+
message: t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.error\`),
|
|
307
308
|
}),`);
|
|
308
309
|
}
|
|
309
310
|
// Add relationship property fields to schema
|
|
@@ -503,8 +504,8 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
503
504
|
formElements.push(` <FormInput
|
|
504
505
|
form={form}
|
|
505
506
|
id="name"
|
|
506
|
-
name={t(\`features.${names.camelCase}.fields.name.label\`)}
|
|
507
|
-
placeholder={t(\`features.${names.camelCase}.fields.name.placeholder\`)}
|
|
507
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.fields.name.label\`)}
|
|
508
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.fields.name.placeholder\`)}
|
|
508
509
|
isRequired
|
|
509
510
|
/>`);
|
|
510
511
|
}
|
|
@@ -516,7 +517,7 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
516
517
|
|
|
517
518
|
fieldsToInclude.forEach((field) => {
|
|
518
519
|
if (field.name === "content" || field.isContentField) {
|
|
519
|
-
formElements.push(` <FormContainerGeneric form={form} id="${field.name}" name={t(\`features.${names.camelCase}.fields.${field.name}.label\`)}>
|
|
520
|
+
formElements.push(` <FormContainerGeneric form={form} id="${field.name}" name={t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.label\`)}>
|
|
520
521
|
<BlockNoteEditorContainer
|
|
521
522
|
id={form.getValues("id")}
|
|
522
523
|
type="${names.camelCase}"
|
|
@@ -524,7 +525,7 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
524
525
|
onChange={(content, isEmpty, hasUnresolvedDiff) => {
|
|
525
526
|
form.setValue("${field.name}", content);
|
|
526
527
|
}}
|
|
527
|
-
placeholder={t(\`features.${names.camelCase}.fields.${field.name}.placeholder\`)}
|
|
528
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.placeholder\`)}
|
|
528
529
|
bordered
|
|
529
530
|
/>
|
|
530
531
|
</FormContainerGeneric>`);
|
|
@@ -533,8 +534,8 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
533
534
|
formElements.push(` <FormInput
|
|
534
535
|
form={form}
|
|
535
536
|
id="${field.name}"
|
|
536
|
-
name={t(\`features.${names.camelCase}.fields.${field.name}.label\`)}
|
|
537
|
-
placeholder={t(\`features.${names.camelCase}.fields.${field.name}.placeholder\`)}${isRequired ? "\n isRequired" : ""}
|
|
537
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.label\`)}
|
|
538
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.fields.${field.name}.placeholder\`)}${isRequired ? "\n isRequired" : ""}
|
|
538
539
|
/>`);
|
|
539
540
|
}
|
|
540
541
|
});
|
|
@@ -547,13 +548,14 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
547
548
|
}
|
|
548
549
|
|
|
549
550
|
const fieldId = toCamelCase(rel.variant || rel.name);
|
|
551
|
+
const fieldIdLower = fieldId.toLowerCase();
|
|
550
552
|
|
|
551
553
|
if (rel.single) {
|
|
552
554
|
formElements.push(` <${rel.name}Selector
|
|
553
555
|
id="${fieldId}"
|
|
554
556
|
form={form}
|
|
555
|
-
label={t(\`features.${names.camelCase}.relationships.${
|
|
556
|
-
placeholder={t(\`features.${names.camelCase}.relationships.${
|
|
557
|
+
label={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.label\`)}
|
|
558
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.placeholder\`)}${!rel.nullable ? "\n isRequired" : ""}
|
|
557
559
|
/>`);
|
|
558
560
|
// Add form inputs for relationship property fields
|
|
559
561
|
if (rel.fields && rel.fields.length > 0) {
|
|
@@ -564,8 +566,8 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
564
566
|
formElements.push(` <FormInput
|
|
565
567
|
form={form}
|
|
566
568
|
id="${field.name}"
|
|
567
|
-
name={t(\`features.${names.camelCase}.relationships.${
|
|
568
|
-
placeholder={t(\`features.${names.camelCase}.relationships.${
|
|
569
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.label\`)}
|
|
570
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.placeholder\`)}
|
|
569
571
|
type="number"${isRequired ? "\n isRequired" : ""}
|
|
570
572
|
/>`);
|
|
571
573
|
break;
|
|
@@ -573,7 +575,7 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
573
575
|
formElements.push(` <FormCheckbox
|
|
574
576
|
form={form}
|
|
575
577
|
id="${field.name}"
|
|
576
|
-
name={t(\`features.${names.camelCase}.relationships.${
|
|
578
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.label\`)}
|
|
577
579
|
/>`);
|
|
578
580
|
break;
|
|
579
581
|
case "date":
|
|
@@ -581,7 +583,7 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
581
583
|
formElements.push(` <FormDatePicker
|
|
582
584
|
form={form}
|
|
583
585
|
id="${field.name}"
|
|
584
|
-
name={t(\`features.${names.camelCase}.relationships.${
|
|
586
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.label\`)}${isRequired ? "\n isRequired" : ""}
|
|
585
587
|
/>`);
|
|
586
588
|
break;
|
|
587
589
|
case "string":
|
|
@@ -590,8 +592,8 @@ function generateFormFields(data: FrontendTemplateData): string {
|
|
|
590
592
|
formElements.push(` <FormInput
|
|
591
593
|
form={form}
|
|
592
594
|
id="${field.name}"
|
|
593
|
-
name={t(\`features.${names.camelCase}.relationships.${
|
|
594
|
-
placeholder={t(\`features.${names.camelCase}.relationships.${
|
|
595
|
+
name={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.label\`)}
|
|
596
|
+
placeholder={t(\`features.${names.camelCase.toLowerCase()}.relationships.${fieldIdLower}.fields.${field.name}.placeholder\`)}${isRequired ? "\n isRequired" : ""}
|
|
595
597
|
/>`);
|
|
596
598
|
break;
|
|
597
599
|
}
|
|
@@ -20,7 +20,7 @@ export function generateI18nKeys(
|
|
|
20
20
|
fields: FrontendField[],
|
|
21
21
|
relationships: FrontendRelationship[]
|
|
22
22
|
): I18nKeySet {
|
|
23
|
-
const lowerModuleName = names.camelCase;
|
|
23
|
+
const lowerModuleName = names.camelCase.toLowerCase();
|
|
24
24
|
|
|
25
25
|
// Generate field keys
|
|
26
26
|
const fieldKeys: I18nKeySet["fields"] = {};
|
|
@@ -36,7 +36,7 @@ export function generateI18nKeys(
|
|
|
36
36
|
const relationshipKeys: I18nKeySet["relationships"] = {};
|
|
37
37
|
relationships.forEach((rel) => {
|
|
38
38
|
const effectiveName = rel.variant || rel.name;
|
|
39
|
-
const effectiveKey = toCamelCase(effectiveName);
|
|
39
|
+
const effectiveKey = toCamelCase(effectiveName).toLowerCase();
|
|
40
40
|
relationshipKeys[effectiveKey] = {
|
|
41
41
|
label: toTitleCase(effectiveName),
|
|
42
42
|
placeholder: `Select ${toTitleCase(effectiveName).toLowerCase()}`,
|
|
@@ -52,25 +52,12 @@ export function updateI18n(
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
// Check if module already exists in features
|
|
56
|
-
if (messages.features && messages.features[names.camelCase]) {
|
|
57
|
-
return {
|
|
58
|
-
success: true,
|
|
59
|
-
message: `Module ${names.camelCase} already exists in messages/${language}.json`,
|
|
60
|
-
alreadyExists: true,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
55
|
// Build the i18n messages for this module
|
|
65
56
|
const moduleMessages = buildI18nMessages(i18nKeys);
|
|
57
|
+
const lowercaseModuleName = names.camelCase.toLowerCase();
|
|
66
58
|
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
messages.features = {};
|
|
70
|
-
}
|
|
71
|
-
messages.features[names.camelCase] = moduleMessages.features[i18nKeys.moduleName];
|
|
72
|
-
|
|
73
|
-
// Add to types section (if not exists)
|
|
59
|
+
// Always ensure types section is updated (even if features already exist)
|
|
60
|
+
let typesUpdated = false;
|
|
74
61
|
if (!messages.types) {
|
|
75
62
|
messages.types = {};
|
|
76
63
|
}
|
|
@@ -78,7 +65,46 @@ export function updateI18n(
|
|
|
78
65
|
const lowercasePluralKey = names.pluralCamel.toLowerCase();
|
|
79
66
|
if (typesKey && !messages.types[lowercasePluralKey]) {
|
|
80
67
|
messages.types[lowercasePluralKey] = moduleMessages.types[typesKey];
|
|
68
|
+
typesUpdated = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Check if module already exists in features
|
|
72
|
+
const featuresAlreadyExist = messages.features && messages.features[lowercaseModuleName];
|
|
73
|
+
|
|
74
|
+
if (featuresAlreadyExist) {
|
|
75
|
+
// Features exist, but we may have added types
|
|
76
|
+
if (typesUpdated) {
|
|
77
|
+
if (dryRun) {
|
|
78
|
+
return {
|
|
79
|
+
success: true,
|
|
80
|
+
message: `[DRY RUN] Module ${names.camelCase} exists, would add types.${lowercasePluralKey}`,
|
|
81
|
+
alreadyExists: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Write updated content (types were added)
|
|
86
|
+
const updatedContent = JSON.stringify(messages, null, 2);
|
|
87
|
+
fs.writeFileSync(messagesPath, updatedContent, "utf-8");
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
message: `Module ${names.camelCase} exists, added types.${lowercasePluralKey}`,
|
|
92
|
+
alreadyExists: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
success: true,
|
|
98
|
+
message: `Module ${names.camelCase} already exists in messages/${language}.json`,
|
|
99
|
+
alreadyExists: true,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Add to features section (new module)
|
|
104
|
+
if (!messages.features) {
|
|
105
|
+
messages.features = {};
|
|
81
106
|
}
|
|
107
|
+
messages.features[lowercaseModuleName] = moduleMessages.features[i18nKeys.moduleName];
|
|
82
108
|
|
|
83
109
|
if (dryRun) {
|
|
84
110
|
return {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { JsonApiDataFactory } from "../core/factories/JsonApiDataFactory";
|
|
4
4
|
import { ApiRequestDataTypeInterface } from "../core/interfaces/ApiRequestDataTypeInterface";
|
|
5
5
|
import { ApiResponseInterface } from "../core/interfaces/ApiResponseInterface";
|
|
6
|
+
import { setBootstrapper } from "../core/registry/bootstrapStore";
|
|
6
7
|
import { translateResponse } from "../core/utils/translateResponse";
|
|
7
8
|
import { ModuleWithPermissions } from "../permissions/types";
|
|
8
9
|
import { directFetch } from "./request";
|
|
@@ -30,6 +31,7 @@ export function configureClientJsonApi(config: {
|
|
|
30
31
|
}): void {
|
|
31
32
|
_clientConfig = config;
|
|
32
33
|
if (config.bootstrapper) {
|
|
34
|
+
setBootstrapper(config.bootstrapper);
|
|
33
35
|
config.bootstrapper();
|
|
34
36
|
}
|
|
35
37
|
}
|
package/src/client/config.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { ModuleWithPermissions } from "../permissions/types";
|
|
4
|
+
import { setBootstrapper } from "../core/registry/bootstrapStore";
|
|
4
5
|
|
|
5
6
|
// Config storage for client-side contexts
|
|
6
7
|
let _clientConfig: {
|
|
@@ -23,8 +24,9 @@ export function configureJsonApi(config: {
|
|
|
23
24
|
additionalHeaders?: Record<string, string>;
|
|
24
25
|
}): void {
|
|
25
26
|
_clientConfig = config;
|
|
26
|
-
//
|
|
27
|
+
// Register and call bootstrapper to register all modules
|
|
27
28
|
if (config.bootstrapper) {
|
|
29
|
+
setBootstrapper(config.bootstrapper);
|
|
28
30
|
config.bootstrapper();
|
|
29
31
|
}
|
|
30
32
|
}
|
|
@@ -9,7 +9,7 @@ export function PageContainer({ children, testId, className }: PageContainerProp
|
|
|
9
9
|
return (
|
|
10
10
|
<div className={`flex h-full w-full flex-col`} data-testid={testId}>
|
|
11
11
|
<Header />
|
|
12
|
-
<main className={cn(`flex w-full flex-1 flex-col gap-y-4 pt-4 pl-4 pr-4`, className)}>{children}</main>
|
|
12
|
+
<main className={cn(`flex w-full flex-1 flex-col gap-y-4 pt-4 pl-4 pr-4 pb-20`, className)}>{children}</main>
|
|
13
13
|
</div>
|
|
14
14
|
);
|
|
15
15
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ApiRequestDataTypeInterface } from "../interfaces/ApiRequestDataTypeInterface";
|
|
2
2
|
import { ModuleWithPermissions } from "../../permissions/types";
|
|
3
|
+
import { tryBootstrap, hasBootstrapper } from "./bootstrapStore";
|
|
3
4
|
|
|
4
5
|
// Foundation module types - defined by LIBRARY
|
|
5
6
|
export interface FoundationModuleDefinitions {
|
|
@@ -33,14 +34,34 @@ class ModuleRegistryClass {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
get<K extends keyof ModuleDefinitions>(name: K): ModuleDefinitions[K] {
|
|
36
|
-
|
|
37
|
+
let module = this._modules.get(name as string);
|
|
38
|
+
|
|
39
|
+
// Self-healing: if module not found, try bootstrapping first
|
|
37
40
|
if (!module) {
|
|
38
|
-
|
|
41
|
+
const didBootstrap = tryBootstrap();
|
|
42
|
+
if (didBootstrap) {
|
|
43
|
+
// Retry after bootstrap
|
|
44
|
+
module = this._modules.get(name as string);
|
|
45
|
+
}
|
|
39
46
|
}
|
|
47
|
+
|
|
48
|
+
if (!module) {
|
|
49
|
+
// Provide helpful error message based on state
|
|
50
|
+
const hint = hasBootstrapper()
|
|
51
|
+
? "Bootstrap was called but module still not found. Check module registration."
|
|
52
|
+
: "No bootstrapper registered. Ensure configureJsonApi({ bootstrapper }) is called before accessing modules.";
|
|
53
|
+
throw new Error(`Module "${String(name)}" not registered. ${hint}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
return module as ModuleDefinitions[K];
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
findByName(moduleName: string): ModuleWithPermissions {
|
|
60
|
+
// Self-healing: try bootstrap if registry is empty
|
|
61
|
+
if (this._modules.size === 0) {
|
|
62
|
+
tryBootstrap();
|
|
63
|
+
}
|
|
64
|
+
|
|
44
65
|
// Search by module's name property (e.g., "topics", "articles")
|
|
45
66
|
for (const module of this._modules.values()) {
|
|
46
67
|
if ((module as ModuleWithPermissions).name === moduleName) {
|
|
@@ -52,7 +73,16 @@ class ModuleRegistryClass {
|
|
|
52
73
|
|
|
53
74
|
findByModelName(modelName: string): ModuleWithPermissions {
|
|
54
75
|
// Direct lookup by registry key (e.g., "Article", "Document")
|
|
55
|
-
|
|
76
|
+
let module = this._modules.get(modelName);
|
|
77
|
+
|
|
78
|
+
// Self-healing: if not found, try bootstrapping
|
|
79
|
+
if (!module) {
|
|
80
|
+
const didBootstrap = tryBootstrap();
|
|
81
|
+
if (didBootstrap) {
|
|
82
|
+
module = this._modules.get(modelName);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
56
86
|
if (!module) {
|
|
57
87
|
throw new Error(`Module not found for model: ${modelName}`);
|
|
58
88
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized bootstrap store accessible from ModuleRegistry.
|
|
3
|
+
* This file has NO external dependencies to avoid circular imports.
|
|
4
|
+
*
|
|
5
|
+
* The bootstrap store allows ModuleRegistry to call the app's bootstrapper
|
|
6
|
+
* when modules are accessed before bootstrap() was called, providing
|
|
7
|
+
* self-healing behavior for module evaluation order issues.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
type BootstrapperFn = () => void;
|
|
11
|
+
|
|
12
|
+
let _bootstrapper: BootstrapperFn | null = null;
|
|
13
|
+
let _bootstrapAttempted = false;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Register the bootstrapper function.
|
|
17
|
+
* Called by configureJsonApi() from client/config, client/JsonApiClient, or unified/JsonApiRequest.
|
|
18
|
+
*/
|
|
19
|
+
export function setBootstrapper(fn: BootstrapperFn): void {
|
|
20
|
+
_bootstrapper = fn;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the registered bootstrapper function.
|
|
25
|
+
* Returns null if no bootstrapper has been registered.
|
|
26
|
+
*/
|
|
27
|
+
export function getBootstrapper(): BootstrapperFn | null {
|
|
28
|
+
return _bootstrapper;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Attempt to run the bootstrapper if one is registered.
|
|
33
|
+
* Returns true if bootstrapper was executed, false if not available.
|
|
34
|
+
* Safe to call multiple times - bootstrapper is expected to be idempotent.
|
|
35
|
+
*/
|
|
36
|
+
export function tryBootstrap(): boolean {
|
|
37
|
+
if (_bootstrapAttempted && !_bootstrapper) {
|
|
38
|
+
// Already tried and no bootstrapper available
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
_bootstrapAttempted = true;
|
|
42
|
+
|
|
43
|
+
if (_bootstrapper) {
|
|
44
|
+
_bootstrapper();
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if a bootstrapper has been registered.
|
|
52
|
+
*/
|
|
53
|
+
export function hasBootstrapper(): boolean {
|
|
54
|
+
return _bootstrapper !== null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Reset the bootstrap store. Useful for testing.
|
|
59
|
+
*/
|
|
60
|
+
export function resetBootstrapStore(): void {
|
|
61
|
+
_bootstrapper = null;
|
|
62
|
+
_bootstrapAttempted = false;
|
|
63
|
+
}
|
|
@@ -2,6 +2,7 @@ import { JsonApiDataFactory } from "../core/factories/JsonApiDataFactory";
|
|
|
2
2
|
import { ApiData } from "../core/interfaces/ApiData";
|
|
3
3
|
import { ApiRequestDataTypeInterface } from "../core/interfaces/ApiRequestDataTypeInterface";
|
|
4
4
|
import { ApiResponseInterface } from "../core/interfaces/ApiResponseInterface";
|
|
5
|
+
import { setBootstrapper } from "../core/registry/bootstrapStore";
|
|
5
6
|
import { translateResponse } from "../core/utils/translateResponse";
|
|
6
7
|
import { ModuleWithPermissions } from "../permissions/types";
|
|
7
8
|
|
|
@@ -58,8 +59,9 @@ export function configureJsonApi(config: {
|
|
|
58
59
|
additionalHeaders?: Record<string, string>;
|
|
59
60
|
}): void {
|
|
60
61
|
_staticConfig = config;
|
|
61
|
-
//
|
|
62
|
+
// Register and call bootstrapper to register all modules
|
|
62
63
|
if (config.bootstrapper) {
|
|
64
|
+
setBootstrapper(config.bootstrapper);
|
|
63
65
|
config.bootstrapper();
|
|
64
66
|
}
|
|
65
67
|
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
var _chunkCKS6SVUKjs = require('./chunk-CKS6SVUK.js');
|
|
12
|
-
require('./chunk-7QVYU63E.js');
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
exports.JsonApiDelete = _chunkCKS6SVUKjs.JsonApiDelete; exports.JsonApiGet = _chunkCKS6SVUKjs.JsonApiGet; exports.JsonApiPatch = _chunkCKS6SVUKjs.JsonApiPatch; exports.JsonApiPost = _chunkCKS6SVUKjs.JsonApiPost; exports.JsonApiPut = _chunkCKS6SVUKjs.JsonApiPut; exports.configureJsonApi = _chunkCKS6SVUKjs.configureJsonApi; exports.getApiUrl = _chunkCKS6SVUKjs.getApiUrl; exports.getAppUrl = _chunkCKS6SVUKjs.getAppUrl; exports.getTrackablePages = _chunkCKS6SVUKjs.getTrackablePages;
|
|
24
|
-
//# sourceMappingURL=JsonApiRequest-2OM5NDAW.js.map
|