@carlonicora/nextjs-jsonapi 6.13.1 → 6.14.1
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-VXMDSQAA.mjs → BlockNoteEditor-I3J6YYXK.mjs} +10 -10
- package/dist/{BlockNoteEditor-UBVEUFNR.js → BlockNoteEditor-UIAX72XW.js} +25 -25
- package/dist/{BlockNoteEditor-UBVEUFNR.js.map → BlockNoteEditor-UIAX72XW.js.map} +1 -1
- package/dist/{BlockNoteViewer-2XYASA5Y.js → BlockNoteViewer-APBOPT4B.js} +8 -8
- package/dist/{BlockNoteViewer-2XYASA5Y.js.map → BlockNoteViewer-APBOPT4B.js.map} +1 -1
- package/dist/{BlockNoteViewer-NBW2H6LC.mjs → BlockNoteViewer-HIUAJQRI.mjs} +7 -7
- package/dist/JsonApiRequest-G4J2WTSU.js +26 -0
- package/dist/{JsonApiRequest-WISJZSDE.js.map → JsonApiRequest-G4J2WTSU.js.map} +1 -1
- package/dist/{JsonApiRequest-F6TMJ42G.mjs → JsonApiRequest-IKKHWCPB.mjs} +2 -2
- package/dist/billing/index.js +497 -497
- package/dist/billing/index.mjs +9 -9
- package/dist/{chunk-7DBJTNSU.mjs → chunk-4RPGFQBA.mjs} +2 -2
- package/dist/{chunk-BZ5J2FIV.js → chunk-4XLDBOSE.js} +11 -11
- package/dist/{chunk-BZ5J2FIV.js.map → chunk-4XLDBOSE.js.map} +1 -1
- package/dist/{chunk-27LQDGNP.mjs → chunk-4ZJIUTIV.mjs} +6 -1
- package/dist/chunk-4ZJIUTIV.mjs.map +1 -0
- package/dist/{chunk-PXDUJ47N.js → chunk-6RCZ64BQ.js} +18 -18
- package/dist/{chunk-PXDUJ47N.js.map → chunk-6RCZ64BQ.js.map} +1 -1
- package/dist/{chunk-7JZHPMGW.mjs → chunk-A3RB47X4.mjs} +44 -20
- package/dist/chunk-A3RB47X4.mjs.map +1 -0
- package/dist/{chunk-DOCIFE6A.mjs → chunk-AUD6ZLBB.mjs} +3 -3
- package/dist/{chunk-GB6DBFYY.js → chunk-DGN3PR2F.js} +3 -3
- package/dist/{chunk-GB6DBFYY.js.map → chunk-DGN3PR2F.js.map} +1 -1
- package/dist/{chunk-7ZHTPFFT.js → chunk-DJXSCARG.js} +15 -15
- package/dist/{chunk-7ZHTPFFT.js.map → chunk-DJXSCARG.js.map} +1 -1
- package/dist/{chunk-QKNGZDSQ.js → chunk-GM4VZTWL.js} +2 -2
- package/dist/{chunk-QKNGZDSQ.js.map → chunk-GM4VZTWL.js.map} +1 -1
- package/dist/{chunk-47BBY5KA.mjs → chunk-HAW5PNJG.mjs} +4 -4
- package/dist/{chunk-YRKIFYOE.mjs → chunk-KTGKW72G.mjs} +27 -8
- package/dist/chunk-KTGKW72G.mjs.map +1 -0
- package/dist/{chunk-PV4Z5W6V.js → chunk-KV4N7DEG.js} +19 -19
- package/dist/{chunk-PV4Z5W6V.js.map → chunk-KV4N7DEG.js.map} +1 -1
- package/dist/{chunk-ZLTOUZN2.mjs → chunk-M3N7LPHX.mjs} +4 -4
- package/dist/{chunk-DGWOVYEC.mjs → chunk-MQ7TXALH.mjs} +2 -2
- package/dist/{chunk-W62MHDWM.js → chunk-N5ZE2Z6I.js} +58 -34
- package/dist/chunk-N5ZE2Z6I.js.map +1 -0
- package/dist/{chunk-2SV4SS6N.js → chunk-OQQ7CZWT.js} +12 -12
- package/dist/{chunk-2SV4SS6N.js.map → chunk-OQQ7CZWT.js.map} +1 -1
- package/dist/{chunk-JJMMT24D.mjs → chunk-OUEYZOLB.mjs} +2 -2
- package/dist/{chunk-XXP3TQHF.mjs → chunk-OY65RYD3.mjs} +2 -2
- package/dist/{chunk-MOGEGWXW.js → chunk-PMESKNKL.js} +868 -868
- package/dist/{chunk-MOGEGWXW.js.map → chunk-PMESKNKL.js.map} +1 -1
- package/dist/{chunk-B6JGKDP2.js → chunk-QGVVF77L.js} +48 -29
- package/dist/chunk-QGVVF77L.js.map +1 -0
- package/dist/{chunk-BH6JWIKI.mjs → chunk-QIEF7HIL.mjs} +2 -2
- package/dist/{chunk-P445QGOC.mjs → chunk-RFKSJIQB.mjs} +7 -7
- package/dist/{chunk-PYRZCKC2.js → chunk-RT7XFGIN.js} +1141 -1137
- package/dist/chunk-RT7XFGIN.js.map +1 -0
- package/dist/{chunk-N4RZZKL5.mjs → chunk-RYY7USSW.mjs} +19 -15
- package/dist/chunk-RYY7USSW.mjs.map +1 -0
- package/dist/{chunk-6JUCIWDC.mjs → chunk-UOTHRJD2.mjs} +2 -2
- package/dist/{chunk-KMW7UNL2.js → chunk-WCEYT3GG.js} +8 -8
- package/dist/{chunk-KMW7UNL2.js.map → chunk-WCEYT3GG.js.map} +1 -1
- package/dist/{chunk-MHB36MYE.js → chunk-WUNSG3IN.js} +7 -2
- package/dist/chunk-WUNSG3IN.js.map +1 -0
- package/dist/{chunk-Z4POQWFG.js → chunk-ZYSNJKOP.js} +4 -4
- package/dist/{chunk-Z4POQWFG.js.map → chunk-ZYSNJKOP.js.map} +1 -1
- package/dist/client/index.js +10 -10
- package/dist/client/index.mjs +9 -9
- package/dist/components/index.js +10 -10
- package/dist/components/index.mjs +9 -9
- package/dist/contexts/index.js +10 -10
- package/dist/contexts/index.mjs +9 -9
- package/dist/core/index.js +7 -7
- package/dist/core/index.mjs +6 -6
- package/dist/email-automations/index.js +99 -99
- package/dist/email-automations/index.mjs +9 -9
- package/dist/features/analytics/index.js +126 -126
- package/dist/features/analytics/index.mjs +9 -9
- package/dist/features/analytics/server.js +10 -10
- package/dist/features/analytics/server.mjs +8 -8
- package/dist/features/blog/index.js +156 -156
- package/dist/features/blog/index.mjs +9 -9
- package/dist/features/blog/server.js +28 -28
- package/dist/features/blog/server.mjs +7 -7
- package/dist/features/help/index.js +43 -43
- package/dist/features/help/index.mjs +9 -9
- package/dist/features/tokenusage/index.js +132 -132
- package/dist/features/tokenusage/index.mjs +9 -9
- package/dist/index.js +8 -8
- package/dist/index.mjs +7 -7
- package/dist/{lifecycle-LKGPRBS7.mjs → lifecycle-KI3PCJSE.mjs} +3 -3
- package/dist/lifecycle-TO65I7TO.js +23 -0
- package/dist/{lifecycle-4GUOTB46.js.map → lifecycle-TO65I7TO.js.map} +1 -1
- package/dist/local-transport-4YDJ6FQG.js +15 -0
- package/dist/{local-transport-7H3SPZSA.js.map → local-transport-4YDJ6FQG.js.map} +1 -1
- package/dist/local-transport-C7YTA4I7.mjs +15 -0
- package/dist/offline/index.d.mts +160 -4
- package/dist/offline/index.d.ts +160 -4
- package/dist/offline/index.js +210 -163
- package/dist/offline/index.js.map +1 -1
- package/dist/offline/index.mjs +76 -29
- package/dist/offline/index.mjs.map +1 -1
- package/dist/request-66FGJIHB.mjs +10 -0
- package/dist/request-6W6UGKJV.js +10 -0
- package/dist/{request-KTBV6HHH.js.map → request-6W6UGKJV.js.map} +1 -1
- package/dist/server/index.js +9 -9
- package/dist/server/index.mjs +8 -8
- package/dist/write-through-BAPS24GL.js +15 -0
- package/dist/{write-through-FDQ7N4YI.js.map → write-through-BAPS24GL.js.map} +1 -1
- package/dist/write-through-IWQKXYHE.mjs +15 -0
- package/package.json +1 -1
- package/src/hooks/__tests__/usePageTracker.spec.tsx +61 -0
- package/src/hooks/usePageTracker.ts +17 -7
- package/src/offline/__tests__/generic-answers.spec.ts +146 -3
- package/src/offline/__tests__/local-transport.spec.ts +15 -1
- package/src/offline/__tests__/offline-fixtures.ts +29 -2
- package/src/offline/__tests__/write-through.spec.ts +27 -1
- package/src/offline/config.ts +9 -0
- package/src/offline/generic-answers.ts +44 -17
- package/src/offline/index.ts +2 -0
- package/src/offline/local-transport.ts +5 -3
- package/src/offline/sync/__tests__/runtime.spec.ts +30 -1
- package/src/offline/sync/__tests__/sync-fixtures.ts +22 -0
- package/src/offline/sync/__tests__/sync-root.spec.ts +184 -1
- package/src/offline/sync/known-roots.ts +29 -7
- package/src/offline/sync/runtime.ts +3 -2
- package/src/offline/sync/sync-root.ts +70 -19
- package/src/offline/types.ts +39 -1
- package/src/offline/write-through.ts +6 -3
- package/dist/JsonApiRequest-WISJZSDE.js +0 -26
- package/dist/chunk-27LQDGNP.mjs.map +0 -1
- package/dist/chunk-7JZHPMGW.mjs.map +0 -1
- package/dist/chunk-B6JGKDP2.js.map +0 -1
- package/dist/chunk-MHB36MYE.js.map +0 -1
- package/dist/chunk-N4RZZKL5.mjs.map +0 -1
- package/dist/chunk-PYRZCKC2.js.map +0 -1
- package/dist/chunk-W62MHDWM.js.map +0 -1
- package/dist/chunk-YRKIFYOE.mjs.map +0 -1
- package/dist/lifecycle-4GUOTB46.js +0 -23
- package/dist/local-transport-7H3SPZSA.js +0 -15
- package/dist/local-transport-QYFKA6AN.mjs +0 -15
- package/dist/request-KTBV6HHH.js +0 -10
- package/dist/request-SQOUNWZ4.mjs +0 -10
- package/dist/write-through-FDQ7N4YI.js +0 -15
- package/dist/write-through-R25VQMH6.mjs +0 -15
- /package/dist/{BlockNoteEditor-VXMDSQAA.mjs.map → BlockNoteEditor-I3J6YYXK.mjs.map} +0 -0
- /package/dist/{BlockNoteViewer-NBW2H6LC.mjs.map → BlockNoteViewer-HIUAJQRI.mjs.map} +0 -0
- /package/dist/{JsonApiRequest-F6TMJ42G.mjs.map → JsonApiRequest-IKKHWCPB.mjs.map} +0 -0
- /package/dist/{chunk-7DBJTNSU.mjs.map → chunk-4RPGFQBA.mjs.map} +0 -0
- /package/dist/{chunk-DOCIFE6A.mjs.map → chunk-AUD6ZLBB.mjs.map} +0 -0
- /package/dist/{chunk-47BBY5KA.mjs.map → chunk-HAW5PNJG.mjs.map} +0 -0
- /package/dist/{chunk-ZLTOUZN2.mjs.map → chunk-M3N7LPHX.mjs.map} +0 -0
- /package/dist/{chunk-DGWOVYEC.mjs.map → chunk-MQ7TXALH.mjs.map} +0 -0
- /package/dist/{chunk-JJMMT24D.mjs.map → chunk-OUEYZOLB.mjs.map} +0 -0
- /package/dist/{chunk-XXP3TQHF.mjs.map → chunk-OY65RYD3.mjs.map} +0 -0
- /package/dist/{chunk-BH6JWIKI.mjs.map → chunk-QIEF7HIL.mjs.map} +0 -0
- /package/dist/{chunk-P445QGOC.mjs.map → chunk-RFKSJIQB.mjs.map} +0 -0
- /package/dist/{chunk-6JUCIWDC.mjs.map → chunk-UOTHRJD2.mjs.map} +0 -0
- /package/dist/{lifecycle-LKGPRBS7.mjs.map → lifecycle-KI3PCJSE.mjs.map} +0 -0
- /package/dist/{local-transport-QYFKA6AN.mjs.map → local-transport-C7YTA4I7.mjs.map} +0 -0
- /package/dist/{request-SQOUNWZ4.mjs.map → request-66FGJIHB.mjs.map} +0 -0
- /package/dist/{write-through-R25VQMH6.mjs.map → write-through-IWQKXYHE.mjs.map} +0 -0
package/dist/billing/index.mjs
CHANGED
|
@@ -81,11 +81,11 @@ import {
|
|
|
81
81
|
useCurrentUserContext,
|
|
82
82
|
useDataListRetriever,
|
|
83
83
|
usePageUrlGenerator
|
|
84
|
-
} from "../chunk-
|
|
84
|
+
} from "../chunk-RYY7USSW.mjs";
|
|
85
85
|
import "../chunk-VOXD3ZLY.mjs";
|
|
86
86
|
import {
|
|
87
87
|
getStripePublishableKey
|
|
88
|
-
} from "../chunk-
|
|
88
|
+
} from "../chunk-HAW5PNJG.mjs";
|
|
89
89
|
import {
|
|
90
90
|
getRoleId
|
|
91
91
|
} from "../chunk-PW42LRSL.mjs";
|
|
@@ -112,17 +112,17 @@ import {
|
|
|
112
112
|
resolveChangePlanTarget,
|
|
113
113
|
showToast,
|
|
114
114
|
toPromotionCodeResult
|
|
115
|
-
} from "../chunk-
|
|
116
|
-
import "../chunk-
|
|
115
|
+
} from "../chunk-RFKSJIQB.mjs";
|
|
116
|
+
import "../chunk-OY65RYD3.mjs";
|
|
117
117
|
import {
|
|
118
118
|
Modules,
|
|
119
119
|
useI18nLocale,
|
|
120
120
|
useI18nRouter
|
|
121
|
-
} from "../chunk-
|
|
122
|
-
import "../chunk-
|
|
123
|
-
import "../chunk-
|
|
124
|
-
import "../chunk-
|
|
125
|
-
import "../chunk-
|
|
121
|
+
} from "../chunk-UOTHRJD2.mjs";
|
|
122
|
+
import "../chunk-AUD6ZLBB.mjs";
|
|
123
|
+
import "../chunk-M3N7LPHX.mjs";
|
|
124
|
+
import "../chunk-4ZJIUTIV.mjs";
|
|
125
|
+
import "../chunk-MQ7TXALH.mjs";
|
|
126
126
|
import "../chunk-AUXK7QSA.mjs";
|
|
127
127
|
import {
|
|
128
128
|
__name
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
markOfflineUnavailable,
|
|
10
10
|
refreshOfflineDeviceState,
|
|
11
11
|
unblockOfflineStore
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-4ZJIUTIV.mjs";
|
|
13
13
|
import {
|
|
14
14
|
__name
|
|
15
15
|
} from "./chunk-PAWJFY3S.mjs";
|
|
@@ -1046,4 +1046,4 @@ export {
|
|
|
1046
1046
|
onUserSignedIn,
|
|
1047
1047
|
isRootLocked
|
|
1048
1048
|
};
|
|
1049
|
-
//# sourceMappingURL=chunk-
|
|
1049
|
+
//# sourceMappingURL=chunk-4RPGFQBA.mjs.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkGM4VZTWLjs = require('./chunk-GM4VZTWL.js');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
var _chunk7QVYU63Ejs = require('./chunk-7QVYU63E.js');
|
|
@@ -30,11 +30,11 @@ var RehydrationFactory = class {
|
|
|
30
30
|
_chunk7QVYU63Ejs.__name.call(void 0, this, "RehydrationFactory");
|
|
31
31
|
}
|
|
32
32
|
static rehydrate(classKey, data) {
|
|
33
|
-
const factoryClass =
|
|
33
|
+
const factoryClass = _chunkGM4VZTWLjs.DataClassRegistry.get(classKey);
|
|
34
34
|
return guarded(data, () => new factoryClass().rehydrate(data));
|
|
35
35
|
}
|
|
36
36
|
static rehydrateList(classKey, data) {
|
|
37
|
-
const factoryClass =
|
|
37
|
+
const factoryClass = _chunkGM4VZTWLjs.DataClassRegistry.get(classKey);
|
|
38
38
|
return data.map(
|
|
39
39
|
(item) => guarded(item, () => new factoryClass().rehydrate(item))
|
|
40
40
|
);
|
|
@@ -336,13 +336,13 @@ var ModuleRegistryClass = class {
|
|
|
336
336
|
get(name) {
|
|
337
337
|
let module = this._modules.get(name);
|
|
338
338
|
if (!module) {
|
|
339
|
-
const didBootstrap =
|
|
339
|
+
const didBootstrap = _chunkGM4VZTWLjs.tryBootstrap.call(void 0, );
|
|
340
340
|
if (didBootstrap) {
|
|
341
341
|
module = this._modules.get(name);
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
344
|
if (!module) {
|
|
345
|
-
const hint =
|
|
345
|
+
const hint = _chunkGM4VZTWLjs.hasBootstrapper.call(void 0, ) ? "Bootstrap was called but module still not found. Check module registration." : "No bootstrapper registered. Ensure configureJsonApi({ bootstrapper }) is called before accessing modules.";
|
|
346
346
|
throw new Error(`Module "${String(name)}" not registered. ${hint}`);
|
|
347
347
|
}
|
|
348
348
|
return module;
|
|
@@ -352,12 +352,12 @@ var ModuleRegistryClass = class {
|
|
|
352
352
|
* can check for an opt-in module the app may not have registered.
|
|
353
353
|
*/
|
|
354
354
|
has(name) {
|
|
355
|
-
if (this._modules.size === 0)
|
|
355
|
+
if (this._modules.size === 0) _chunkGM4VZTWLjs.tryBootstrap.call(void 0, );
|
|
356
356
|
return this._modules.has(name);
|
|
357
357
|
}
|
|
358
358
|
findByName(moduleName) {
|
|
359
359
|
if (this._modules.size === 0) {
|
|
360
|
-
|
|
360
|
+
_chunkGM4VZTWLjs.tryBootstrap.call(void 0, );
|
|
361
361
|
}
|
|
362
362
|
for (const module of this._modules.values()) {
|
|
363
363
|
if (module.name === moduleName) {
|
|
@@ -368,7 +368,7 @@ var ModuleRegistryClass = class {
|
|
|
368
368
|
}
|
|
369
369
|
getAllPageUrls() {
|
|
370
370
|
if (this._modules.size === 0) {
|
|
371
|
-
|
|
371
|
+
_chunkGM4VZTWLjs.tryBootstrap.call(void 0, );
|
|
372
372
|
}
|
|
373
373
|
const seen = /* @__PURE__ */ new Set();
|
|
374
374
|
const result = [];
|
|
@@ -385,7 +385,7 @@ var ModuleRegistryClass = class {
|
|
|
385
385
|
findByModelName(modelName) {
|
|
386
386
|
let module = this._modules.get(modelName);
|
|
387
387
|
if (!module) {
|
|
388
|
-
const didBootstrap =
|
|
388
|
+
const didBootstrap = _chunkGM4VZTWLjs.tryBootstrap.call(void 0, );
|
|
389
389
|
if (didBootstrap) {
|
|
390
390
|
module = this._modules.get(modelName);
|
|
391
391
|
}
|
|
@@ -407,7 +407,7 @@ var ModuleRegistryClass = class {
|
|
|
407
407
|
}
|
|
408
408
|
getAll() {
|
|
409
409
|
if (this._modules.size === 0) {
|
|
410
|
-
|
|
410
|
+
_chunkGM4VZTWLjs.tryBootstrap.call(void 0, );
|
|
411
411
|
}
|
|
412
412
|
return Array.from(this._modules.values());
|
|
413
413
|
}
|
|
@@ -567,4 +567,4 @@ var RootSync = (_class2 = class extends AbstractApiData {constructor(...args) {
|
|
|
567
567
|
|
|
568
568
|
|
|
569
569
|
exports.RehydrationFactory = RehydrationFactory; exports.AbstractApiData = AbstractApiData; exports.configureI18n = configureI18n; exports.useI18nRouter = useI18nRouter; exports.useI18nTranslations = useI18nTranslations; exports.getI18nLink = getI18nLink; exports.useI18nLocale = useI18nLocale; exports.getI18nServerLocale = getI18nServerLocale; exports.useI18nDateFnsLocale = useI18nDateFnsLocale; exports.getI18nSupportedLocales = getI18nSupportedLocales; exports.getI18nDefaultLocale = getI18nDefaultLocale; exports.ModuleRegistry = ModuleRegistry; exports.Modules = Modules; exports.EndpointCreator = EndpointCreator; exports.ROOT_SYNC_ENDPOINT = ROOT_SYNC_ENDPOINT; exports.RootSync = RootSync;
|
|
570
|
-
//# sourceMappingURL=chunk-
|
|
570
|
+
//# sourceMappingURL=chunk-4XLDBOSE.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-BZ5J2FIV.js","../src/core/factories/RehydrationFactory.ts","../src/core/abstracts/AbstractApiData.ts","../src/i18n/config.ts","../src/core/registry/ModuleRegistry.ts","../src/core/endpoint/EndpointCreator.ts","../src/offline/sync/models/RootSync.ts"],"names":["includedData","includedItem","module","entity"],"mappings":"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","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-BZ5J2FIV.js","sourcesContent":[null,"import { ApiDataInterface } from \"../interfaces/ApiDataInterface\";\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { JsonApiHydratedDataInterface } from \"../interfaces/JsonApiHydratedDataInterface\";\nimport { DataClassRegistry } from \"../registry/DataClassRegistry\";\n\n/**\n * The resources being rehydrated right now, along the current chain of relationships. A resource\n * met again inside its own chain (A's relationship leads to B, B's back to A) is a circle in the\n * `included` data: following it would recurse until the stack overflows, so it is skipped there.\n */\nconst inProgress = new Set<string>();\n\nconst keyOf = (data: JsonApiHydratedDataInterface): string | undefined => {\n const resource = data?.jsonApi;\n return resource?.type && resource?.id ? `${resource.type}:${resource.id}` : undefined;\n};\n\nfunction guarded<T>(data: JsonApiHydratedDataInterface, run: () => T): T {\n const key = keyOf(data);\n if (!key) return run();\n if (inProgress.has(key)) return undefined as T;\n inProgress.add(key);\n try {\n return run();\n } finally {\n inProgress.delete(key);\n }\n}\n\nexport class RehydrationFactory {\n public static rehydrate<T extends ApiDataInterface>(\n classKey: ApiRequestDataTypeInterface,\n data: JsonApiHydratedDataInterface,\n ): T {\n const factoryClass = DataClassRegistry.get(classKey);\n\n return guarded(data, () => new factoryClass().rehydrate(data) as T);\n }\n\n public static rehydrateList<T extends ApiDataInterface>(\n classKey: ApiRequestDataTypeInterface,\n data: JsonApiHydratedDataInterface[],\n ): T[] {\n const factoryClass = DataClassRegistry.get(classKey);\n\n return data.map((item: JsonApiHydratedDataInterface) =>\n guarded(item, () => new factoryClass().rehydrate(item) as T),\n );\n }\n}\n","import { RehydrationFactory } from \"../factories/RehydrationFactory\";\nimport { ApiDataInterface } from \"../interfaces/ApiDataInterface\";\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { JsonApiHydratedDataInterface } from \"../interfaces/JsonApiHydratedDataInterface\";\n\nexport abstract class AbstractApiData implements ApiDataInterface {\n static identifierFields: string[] = [\"name\"];\n\n get identifier(): string {\n const fields = (this.constructor as typeof AbstractApiData).identifierFields;\n return fields\n .map((field) => this._jsonApi?.attributes?.[field])\n .filter((v) => v != null && v !== \"\")\n .join(\" \");\n }\n\n protected _jsonApi?: any;\n protected _included?: any[];\n\n protected _id?: string;\n protected _type?: string;\n protected _createdAt?: Date;\n protected _updatedAt?: Date;\n\n protected _self?: string;\n\n get type(): string {\n if (!this._type) throw new Error(\"Type is not set.\");\n return this._type;\n }\n\n get id(): string {\n if (!this._id) throw new Error(\"Id is not set.\");\n return this._id;\n }\n\n get self(): string | undefined {\n return this._self;\n }\n\n get createdAt(): Date {\n return this._createdAt ?? new Date();\n }\n\n get updatedAt(): Date {\n return this._updatedAt ?? new Date();\n }\n\n get included(): any[] {\n return this._included ?? [];\n }\n\n get jsonApi(): any {\n return this._jsonApi;\n }\n\n ingestJsonApi(_data: JsonApiHydratedDataInterface): void {}\n\n generateApiUrl(_params?: any): string {\n throw new Error(\"Method not implemented.\");\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"Method not implemented.\");\n }\n\n protected _readIncluded<T extends ApiDataInterface>(\n data: JsonApiHydratedDataInterface,\n type: string,\n dataType: ApiRequestDataTypeInterface,\n ): T | T[] | undefined {\n if (\n data.included === undefined ||\n data.included.length === 0 ||\n data.jsonApi.relationships === undefined ||\n data.jsonApi.relationships[type] === undefined ||\n data.jsonApi.relationships[type].data === undefined\n ) {\n return undefined;\n }\n\n if (Array.isArray(data.jsonApi.relationships[type].data)) {\n const response: T[] = data.jsonApi.relationships[type].data.map((jsonApiData: any) => {\n const includedData = data.included.find(\n (includedData: any) => includedData.id === jsonApiData.id && includedData.type === jsonApiData.type,\n );\n\n if (includedData === undefined) return undefined;\n\n return RehydrationFactory.rehydrate(dataType, {\n jsonApi: includedData,\n included: data.included,\n }) as T;\n });\n\n return response.filter((item: T | undefined) => item !== undefined) as T[];\n }\n\n const includedData = data.included.find(\n (includedData: any) =>\n includedData.id === data.jsonApi.relationships[type].data.id &&\n includedData.type === data.jsonApi.relationships[type].data.type,\n );\n\n if (includedData === undefined && data.allData !== undefined) {\n // Try to find in allData as a fallback\n const fallbackData = data.allData.find(\n (includedData: any) =>\n includedData.id === data.jsonApi.relationships[type].data.id &&\n includedData.type === data.jsonApi.relationships[type].data.type,\n );\n if (fallbackData !== undefined) {\n return RehydrationFactory.rehydrate(dataType, {\n jsonApi: fallbackData,\n included: data.included,\n }) as T;\n }\n }\n\n if (includedData === undefined) return undefined;\n\n return RehydrationFactory.rehydrate(dataType, {\n jsonApi: includedData,\n included: data.included,\n }) as T;\n }\n\n /**\n * Read included relationships with polymorphic type resolution.\n * Determines the correct model class based on the JSON:API type in included data.\n */\n protected _readIncludedPolymorphic<T extends ApiDataInterface>(\n data: JsonApiHydratedDataInterface,\n relationshipKey: string,\n candidateModules: ApiRequestDataTypeInterface[],\n ): T | T[] | undefined {\n // Validate relationship exists\n if (!data.included?.length || !data.jsonApi.relationships?.[relationshipKey]?.data) {\n return undefined;\n }\n\n const relData = data.jsonApi.relationships[relationshipKey].data;\n\n // Build lookup map: JSON:API type -> module\n const typeToModule = new Map<string, ApiRequestDataTypeInterface>();\n for (const mod of candidateModules) {\n typeToModule.set(mod.name, mod);\n }\n\n if (Array.isArray(relData)) {\n // Many relationship\n const result: T[] = [];\n for (const item of relData) {\n const includedItem = data.included.find((inc: any) => inc.id === item.id && inc.type === item.type);\n if (!includedItem) continue;\n\n // Resolve module by JSON:API type\n const module = typeToModule.get(includedItem.type);\n if (!module) continue;\n\n result.push(\n RehydrationFactory.rehydrate(module, {\n jsonApi: includedItem,\n included: data.included,\n }) as T,\n );\n }\n return result.length > 0 ? result : undefined;\n }\n\n // Single relationship\n const includedItem = data.included.find((inc: any) => inc.id === relData.id && inc.type === relData.type);\n if (!includedItem) return undefined;\n\n const module = typeToModule.get(includedItem.type);\n if (!module) return undefined;\n\n return RehydrationFactory.rehydrate(module, {\n jsonApi: includedItem,\n included: data.included,\n }) as T;\n }\n\n /**\n * Read included relationship data and augment with relationship meta properties.\n * Handles both single relationships (one-to-one) and array relationships (one-to-many).\n *\n * For single relationships: meta is read from `relationships[type].meta`\n * For array relationships: per-item meta is read from `relationships[type].data[].meta`\n *\n * @param data - Hydrated JSON:API data\n * @param type - Relationship type key (e.g., \"guide\", \"persons\")\n * @param dataType - Module reference for rehydration\n * @returns Related object(s) augmented with meta properties, or undefined\n */\n protected _readIncludedWithMeta<T extends ApiDataInterface, M extends Record<string, any>>(\n data: JsonApiHydratedDataInterface,\n type: string,\n dataType: ApiRequestDataTypeInterface,\n ): (T & M) | (T & M)[] | undefined {\n // Check if relationship exists\n if (\n data.included === undefined ||\n data.included.length === 0 ||\n data.jsonApi.relationships === undefined ||\n data.jsonApi.relationships[type] === undefined ||\n data.jsonApi.relationships[type].data === undefined\n ) {\n return undefined;\n }\n\n const relationshipData = data.jsonApi.relationships[type].data;\n\n // Handle array relationships with per-item meta\n if (Array.isArray(relationshipData)) {\n const result: (T & M)[] = [];\n\n for (const item of relationshipData) {\n const includedData = data.included.find((inc: any) => inc.id === item.id && inc.type === item.type);\n\n if (!includedData) continue;\n\n const entity = RehydrationFactory.rehydrate(dataType, {\n jsonApi: includedData,\n included: data.included,\n }) as T;\n\n // Merge per-item meta from relationships[type].data[].meta\n if (item.meta) {\n AbstractApiData._mergeMeta(entity, item.meta);\n }\n\n result.push(entity as T & M);\n }\n\n return result;\n }\n\n // Handle single relationship\n const includedData = data.included.find(\n (inc: any) => inc.id === relationshipData.id && inc.type === relationshipData.type,\n );\n\n if (!includedData) {\n // Try to find in allData as a fallback\n if (data.allData !== undefined) {\n const fallbackData = data.allData.find(\n (inc: any) => inc.id === relationshipData.id && inc.type === relationshipData.type,\n );\n if (fallbackData) {\n const entity = RehydrationFactory.rehydrate(dataType, {\n jsonApi: fallbackData,\n included: data.included,\n }) as T;\n\n // Merge relationship-level meta for single relationships\n const relationshipMeta = data.jsonApi.relationships[type].meta;\n if (relationshipMeta) {\n AbstractApiData._mergeMeta(entity, relationshipMeta);\n }\n return entity as T & M;\n }\n }\n return undefined;\n }\n\n const entity = RehydrationFactory.rehydrate(dataType, {\n jsonApi: includedData,\n included: data.included,\n }) as T;\n\n // Merge relationship-level meta for single relationships\n const relationshipMeta = data.jsonApi.relationships[type].meta;\n if (relationshipMeta) {\n AbstractApiData._mergeMeta(entity, relationshipMeta);\n }\n\n return entity as T & M;\n }\n\n /**\n * Copies relationship meta onto a rehydrated entity as own data properties. Object.assign throws\n * when the model declares a getter-only accessor of the same name (an app's `Chunk` with\n * `get relevance()`); an own property shadows that getter, so the model reads the meta value.\n */\n private static _mergeMeta(entity: object, meta: Record<string, any>): void {\n for (const [key, value] of Object.entries(meta)) {\n Object.defineProperty(entity, key, { value, writable: true, enumerable: true, configurable: true });\n }\n }\n\n dehydrate(): JsonApiHydratedDataInterface {\n return {\n jsonApi: this._jsonApi,\n included: this._included ?? [],\n };\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n this._jsonApi = data.jsonApi;\n this._included = data.included;\n\n this._type = this._jsonApi.type;\n this._id = this._jsonApi.id;\n this._createdAt = this._jsonApi.meta?.createdAt !== undefined ? new Date(this._jsonApi.meta.createdAt) : undefined;\n this._updatedAt = this._jsonApi.meta?.updatedAt !== undefined ? new Date(this._jsonApi.meta.updatedAt) : undefined;\n\n this._self = this._jsonApi.links?.self ?? undefined;\n\n return this;\n }\n}\n","import { ComponentType } from \"react\";\n\n// Types for injected hooks\nexport interface I18nRouter {\n push: (href: string) => void;\n replace: (href: string) => void;\n back: () => void;\n forward: () => void;\n refresh: () => void;\n prefetch: (href: string) => void;\n}\n\nexport type UseRouterHook = () => I18nRouter;\nexport type UseTranslationsHook = (namespace?: string) => (key: string, values?: Record<string, any>) => string;\nexport type UseLocaleHook = () => string;\nexport type GetLocaleFunction = () => Promise<string>;\n\nexport type UseDateFnsLocaleHook = () => any; // date-fns Locale type\nexport type LinkComponent = ComponentType<{ href: string; children: React.ReactNode; [key: string]: any }>;\n\nexport interface I18nConfig {\n useRouter: UseRouterHook;\n useTranslations: UseTranslationsHook;\n useLocale?: UseLocaleHook;\n useDateFnsLocale?: UseDateFnsLocaleHook;\n Link: LinkComponent;\n usePathname: () => string;\n /**\n * Server-side locale for API requests (pass next-intl's `getLocale` from `next-intl/server`).\n *\n * The library never imports next-intl itself: instrumentation loads the library, and\n * next-intl's `getLocale` pulls in the app's `i18n/request.ts`, which reads\n * `next/root-params` — a module only allowed inside the app directory.\n */\n getLocale: GetLocaleFunction;\n /**\n * Locales the app supports. The first entry is the fallback.\n * Defaults to `[\"en\"]` when omitted.\n */\n supportedLocales?: string[];\n}\n\n// Private storage\nlet _config: I18nConfig | null = null;\n\n// Configuration function (called by app at startup)\nexport function configureI18n(config: I18nConfig): void {\n _config = config;\n}\n\n// Hooks for library components to use\nexport function useI18nRouter(): I18nRouter {\n if (!_config?.useRouter) {\n throw new Error(\"i18n not configured. Call configureI18n() at app startup.\");\n }\n return _config.useRouter();\n}\n\nexport function useI18nTranslations(namespace?: string): (key: string, values?: Record<string, any>) => string {\n if (!_config?.useTranslations) {\n // Fallback: return key as-is (safe for server/client)\n return (key: string) => key;\n }\n return _config.useTranslations(namespace);\n}\n\nexport function getI18nLink(): LinkComponent {\n if (!_config?.Link) {\n throw new Error(\"i18n not configured. Call configureI18n() at app startup.\");\n }\n return _config.Link;\n}\n\nexport function useI18nLocale(): string {\n if (_config?.useLocale) {\n return _config.useLocale();\n }\n // Fallback to English (safe for server/client)\n return \"en\";\n}\n\n/** The server-side locale from the app's `getLocale`. Throws when `configureI18n` has not run. */\nexport async function getI18nServerLocale(): Promise<string> {\n if (!_config?.getLocale) {\n throw new Error(\"i18n not configured. Call configureI18n() with getLocale at app startup.\");\n }\n return _config.getLocale();\n}\n\nexport function useI18nDateFnsLocale(): any {\n if (_config?.useDateFnsLocale) {\n return _config.useDateFnsLocale();\n }\n // Fallback to undefined (Calendar will use default)\n return undefined;\n}\n\n/** The app's supported locales. Defaults to `[\"en\"]` when not configured. */\nexport function getI18nSupportedLocales(): string[] {\n if (_config?.supportedLocales && _config.supportedLocales.length > 0) {\n return _config.supportedLocales;\n }\n return [\"en\"];\n}\n\n/** The app's default locale: the first supported locale (`\"en\"` when not configured). */\nexport function getI18nDefaultLocale(): string {\n return getI18nSupportedLocales()[0];\n}\n","import { ModuleWithPermissions } from \"../../permissions/types\";\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { hasBootstrapper, tryBootstrap } from \"./bootstrapStore\";\n\n// Foundation module types - defined by LIBRARY\nexport interface FoundationModuleDefinitions {\n S3: ModuleWithPermissions;\n Auth: ModuleWithPermissions;\n User: ModuleWithPermissions;\n Author: ModuleWithPermissions;\n Company: ModuleWithPermissions;\n Role: ModuleWithPermissions;\n Notification: ModuleWithPermissions;\n Push: ModuleWithPermissions;\n Feature: ModuleWithPermissions;\n Module: ModuleWithPermissions;\n Content: ModuleWithPermissions;\n HowTo: ModuleWithPermissions;\n HandbookPage: ModuleWithPermissions;\n HandbookSection: ModuleWithPermissions;\n HandbookThread: ModuleWithPermissions;\n HandbookThreadMessage: ModuleWithPermissions;\n Assistant: ModuleWithPermissions;\n AssistantMessage: ModuleWithPermissions;\n AssistantAction: ModuleWithPermissions;\n Chunk: ModuleWithPermissions;\n // Billing modules - READ: all users, UPDATE: CompanyAdministrator, ADMIN: Administrator\n Billing: ModuleWithPermissions;\n StripeCustomer: ModuleWithPermissions;\n StripePaymentMethod: ModuleWithPermissions;\n StripeSubscription: ModuleWithPermissions;\n StripeInvoice: ModuleWithPermissions;\n StripeProduct: ModuleWithPermissions;\n StripePrice: ModuleWithPermissions;\n StripeUsage: ModuleWithPermissions;\n StripePromotionCode: ModuleWithPermissions;\n StripeCoupon: ModuleWithPermissions;\n StripeCouponCode: ModuleWithPermissions;\n StripeCouponOffer: ModuleWithPermissions;\n // AI connection administration\n AiConnection: ModuleWithPermissions;\n // OAuth modules\n OAuth: ModuleWithPermissions;\n // Waitlist modules\n Waitlist: ModuleWithPermissions;\n WaitlistStats: ModuleWithPermissions;\n // Email automation modules\n EmailAutomation: ModuleWithPermissions;\n EmailAutomationStep: ModuleWithPermissions;\n EmailAutomationEnrollment: ModuleWithPermissions;\n EmailAutomationTrigger: ModuleWithPermissions;\n // Email blast modules\n EmailBlast: ModuleWithPermissions;\n EmailBlastDelivery: ModuleWithPermissions;\n EmailBlastAudience: ModuleWithPermissions;\n // Blog modules\n BlogPost: ModuleWithPermissions;\n BlogPostTranslation: ModuleWithPermissions;\n BlogCategory: ModuleWithPermissions;\n BlogCategoryTranslation: ModuleWithPermissions;\n BlogSettings: ModuleWithPermissions;\n // Referral modules\n Referral: ModuleWithPermissions;\n ReferralStats: ModuleWithPermissions;\n // Referral link visits and the administrative referral report (opt-in: registered by apps that spread referralAdminModules)\n ReferralVisit: ModuleWithPermissions;\n ReferralAdminSummary: ModuleWithPermissions;\n ReferralAdminReferrer: ModuleWithPermissions;\n ReferralAdminReferral: ModuleWithPermissions;\n // Two-factor authentication modules\n TotpAuthenticator: ModuleWithPermissions;\n TotpSetup: ModuleWithPermissions;\n TotpVerify: ModuleWithPermissions;\n TotpVerifyLogin: ModuleWithPermissions;\n Passkey: ModuleWithPermissions;\n PasskeyRegistrationOptions: ModuleWithPermissions;\n PasskeyRegistrationVerify: ModuleWithPermissions;\n PasskeyRename: ModuleWithPermissions;\n PasskeyVerifyLogin: ModuleWithPermissions;\n PasskeyAuthenticationOptions: ModuleWithPermissions;\n TwoFactorEnable: ModuleWithPermissions;\n TwoFactorChallenge: ModuleWithPermissions;\n TwoFactorStatus: ModuleWithPermissions;\n BackupCodeVerify: ModuleWithPermissions;\n // OAuth sign-up awaiting consent\n PendingRegistration: ModuleWithPermissions;\n // RBAC modules\n PermissionMapping: ModuleWithPermissions;\n ModulePaths: ModuleWithPermissions;\n RbacMatrix: ModuleWithPermissions;\n // Audit modules\n AuditLog: ModuleWithPermissions;\n // Administrative token-usage reporting modules\n TokenUsageAdminSummary: ModuleWithPermissions;\n TokenUsageAdminTimeline: ModuleWithPermissions;\n TokenUsageAdminBreakdown: ModuleWithPermissions;\n // Self-service token-usage reporting modules\n TokenUsageReportSummary: ModuleWithPermissions;\n TokenUsageReportTimeline: ModuleWithPermissions;\n TokenUsageReportBreakdown: ModuleWithPermissions;\n // First-party web analytics modules (tracker + administrative dashboard)\n AnalyticsEvent: ModuleWithPermissions;\n AnalyticsSummary: ModuleWithPermissions;\n AnalyticsTimeline: ModuleWithPermissions;\n AnalyticsBreakdown: ModuleWithPermissions;\n AnalyticsSession: ModuleWithPermissions;\n AnalyticsPageView: ModuleWithPermissions;\n // Account deletion (opt-in: registered only by apps that enable it)\n AccountDeletion: ModuleWithPermissions;\n // Offline mode (opt-in: registered by apps that spread offlineModules)\n OfflineChangeSet: ModuleWithPermissions;\n RootSync: ModuleWithPermissions;\n}\n\n// App-specific modules - apps will augment this interface ONLY\n\nexport interface AppModuleDefinitions {}\n\n// Combined type for full autocompletion\nexport type ModuleDefinitions = FoundationModuleDefinitions & AppModuleDefinitions;\n\n// Symbol key to avoid conflicts with other globals\nconst MODULES_KEY = Symbol.for(\"nextjs-jsonapi:modules\");\n\n// Use globalThis to persist module registry across HMR reloads\nconst globalStore = globalThis as unknown as {\n [MODULES_KEY]?: Map<string, ApiRequestDataTypeInterface>;\n};\n\n// Initialize global modules map if not set\nif (!globalStore[MODULES_KEY]) {\n globalStore[MODULES_KEY] = new Map();\n}\n\nclass ModuleRegistryClass {\n private get _modules(): Map<string, ApiRequestDataTypeInterface> {\n return globalStore[MODULES_KEY]!;\n }\n\n register<K extends string>(name: K, module: ApiRequestDataTypeInterface): void {\n this._modules.set(name, module);\n }\n\n get<K extends keyof ModuleDefinitions>(name: K): ModuleDefinitions[K] {\n let module = this._modules.get(name as string);\n\n // Self-healing: if module not found, try bootstrapping first\n if (!module) {\n const didBootstrap = tryBootstrap();\n if (didBootstrap) {\n // Retry after bootstrap\n module = this._modules.get(name as string);\n }\n }\n\n if (!module) {\n // Provide helpful error message based on state\n const hint = hasBootstrapper()\n ? \"Bootstrap was called but module still not found. Check module registration.\"\n : \"No bootstrapper registered. Ensure configureJsonApi({ bootstrapper }) is called before accessing modules.\";\n throw new Error(`Module \"${String(name)}\" not registered. ${hint}`);\n }\n\n return module as ModuleDefinitions[K];\n }\n\n /**\n * True when a module is registered under `name`. Never throws, so a component\n * can check for an opt-in module the app may not have registered.\n */\n has(name: string): boolean {\n // Bootstrap only an empty registry (as findByName does): an app that never\n // registers `name` must not re-run its bootstrapper on every check.\n if (this._modules.size === 0) tryBootstrap();\n return this._modules.has(name);\n }\n\n findByName(moduleName: string): ModuleWithPermissions {\n // Self-healing: try bootstrap if registry is empty\n if (this._modules.size === 0) {\n tryBootstrap();\n }\n\n // Search by module's name property (e.g., \"topics\", \"articles\")\n for (const module of this._modules.values()) {\n if ((module as ModuleWithPermissions).name === moduleName) {\n return module as ModuleWithPermissions;\n }\n }\n throw new Error(`Module not found: ${moduleName}`);\n }\n\n getAllPageUrls(): { id: string; text: string }[] {\n if (this._modules.size === 0) {\n tryBootstrap();\n }\n\n const seen = new Set<string>();\n const result: { id: string; text: string }[] = [];\n for (const [key, module] of this._modules.entries()) {\n const m = module as ModuleWithPermissions;\n if (m.pageUrl && !seen.has(m.pageUrl)) {\n seen.add(m.pageUrl);\n result.push({ id: m.pageUrl, text: key });\n result.push({ id: `${m.pageUrl}/:id`, text: `${key} (detail)` });\n }\n }\n return result.sort((a, b) => a.text.localeCompare(b.text));\n }\n\n findByModelName(modelName: string): ModuleWithPermissions {\n // Direct lookup by registry key (e.g., \"Article\", \"Document\")\n let module = this._modules.get(modelName);\n\n // Self-healing: if not found, try bootstrapping\n if (!module) {\n const didBootstrap = tryBootstrap();\n if (didBootstrap) {\n module = this._modules.get(modelName);\n }\n }\n\n if (!module) {\n throw new Error(`Module not found for model: ${modelName}`);\n }\n return module as ModuleWithPermissions;\n }\n\n findByFeature(feature: string): ModuleWithPermissions[] {\n const response: ModuleWithPermissions[] = [];\n\n for (const module of this._modules.values()) {\n const m = module as ModuleWithPermissions;\n if (m.feature === feature) {\n response.push(m);\n }\n }\n\n return response;\n }\n\n getAll(): ApiRequestDataTypeInterface[] {\n if (this._modules.size === 0) {\n tryBootstrap();\n }\n return Array.from(this._modules.values());\n }\n}\n\nexport const ModuleRegistry = new ModuleRegistryClass();\n\n// Proxy object for Modules.X syntax with autocompletion\nexport const Modules = new Proxy({} as ModuleDefinitions, {\n get(_, prop: string) {\n if (prop === \"findByName\") {\n return (name: string) => ModuleRegistry.findByName(name);\n }\n if (prop === \"findByModelName\") {\n return (name: string) => ModuleRegistry.findByModelName(name);\n }\n if (prop === \"findByFeature\") {\n return (feature: string) => ModuleRegistry.findByFeature(feature);\n }\n return ModuleRegistry.get(prop as keyof ModuleDefinitions);\n },\n}) as ModuleDefinitions & {\n findByName: (name: string) => ModuleWithPermissions;\n findByModelName: (name: string) => ModuleWithPermissions;\n findByFeature: (feature: string) => ModuleWithPermissions[];\n};\n","import { FieldSelector } from \"../fields/FieldSelector\";\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\n\nexport type EndpointQuery = {\n endpoint: ApiRequestDataTypeInterface | string;\n id?: string;\n childEndpoint?: ApiRequestDataTypeInterface | string;\n childId?: string;\n additionalParams?: { key: string; value: string | string[] }[];\n};\n\nexport class EndpointCreator {\n private _endpoint: EndpointQuery;\n\n constructor(params: {\n endpoint: ApiRequestDataTypeInterface | string;\n id?: string;\n childEndpoint?: ApiRequestDataTypeInterface | string;\n childId?: string;\n additionalParams?: { key: string; value: string }[];\n }) {\n this._endpoint = {\n endpoint: params.endpoint,\n id: params.id,\n childEndpoint: params.childEndpoint,\n childId: params.childId,\n additionalParams: params.additionalParams ?? [],\n };\n }\n\n endpoint(value: ApiRequestDataTypeInterface | string): EndpointCreator {\n this._endpoint.endpoint = value;\n return this;\n }\n\n id(value: string): EndpointCreator {\n this._endpoint.id = value;\n return this;\n }\n\n childEndpoint(value: ApiRequestDataTypeInterface | string): EndpointCreator {\n this._endpoint.childEndpoint = value;\n return this;\n }\n\n childId(value: string): EndpointCreator {\n this._endpoint.childId = value;\n return this;\n }\n\n set additionalParams(value: { key: string; value: string }[]) {\n this._endpoint.additionalParams = value;\n }\n\n addAdditionalParam(key: string, value: string | string[]): EndpointCreator {\n if (!this._endpoint.additionalParams) this._endpoint.additionalParams = [];\n this._endpoint.additionalParams.push({ key, value });\n return this;\n }\n\n limitToType(selectors: string[]): this {\n this.addAdditionalParam(`include`, selectors.join(\",\"));\n return this;\n }\n\n limitToFields(selectors: FieldSelector<any>[]): this {\n if (selectors.length === 0) return this;\n\n selectors.forEach((selector) => {\n const fieldString = selector.fields.join(\",\");\n this.addAdditionalParam(`fields[${selector.type}]`, fieldString);\n });\n\n return this;\n }\n\n generate(): string {\n let additionalParams = \"\";\n if (this._endpoint.additionalParams) {\n additionalParams = this._endpoint.additionalParams.map((param) => `${param.key}=${param.value}`).join(\"&\");\n }\n\n let response = ``;\n response += `${\n typeof this._endpoint.endpoint === \"string\" ? this._endpoint.endpoint : this._endpoint.endpoint.name\n }`;\n if (this._endpoint.id) response += `/${this._endpoint.id}`;\n if (this._endpoint.childEndpoint) {\n response += `/${\n typeof this._endpoint.childEndpoint === \"string\"\n ? this._endpoint.childEndpoint\n : this._endpoint.childEndpoint.name\n }`;\n }\n if (this._endpoint.childId) response += `/${this._endpoint.childId}`;\n if (additionalParams) response += `?${additionalParams}`;\n\n return response;\n }\n}\n","import { AbstractApiData } from \"../../../core/abstracts/AbstractApiData\";\nimport { JsonApiHydratedDataInterface } from \"../../../core/interfaces/JsonApiHydratedDataInterface\";\nimport { RootSyncEntry, RootSyncInterface, RootSyncLink } from \"./RootSyncInterface\";\n\n/**\n * Child path of the sync answer under the root module record\n * (`GET /<root endpoint>/:id/sync`), the same value as the app API's sync meta endpoint\n * (in Wyrdli, `campaignSyncMeta.endpoint`).\n */\nexport const ROOT_SYNC_ENDPOINT = \"sync\";\n\n/** The sync answer for one root record (`root-syncs`, id = root record id). Read-only. */\nexport class RootSync extends AbstractApiData implements RootSyncInterface {\n private _serverTime?: Date;\n private _locked: boolean = false;\n private _lockedFrom?: Date;\n private _entries?: RootSyncEntry[];\n private _links?: RootSyncLink[];\n\n get serverTime(): Date {\n if (!this._serverTime) throw new Error(\"serverTime is not set.\");\n return this._serverTime;\n }\n\n get locked(): boolean {\n return this._locked;\n }\n\n get lockedFrom(): Date | undefined {\n return this._lockedFrom;\n }\n\n get entries(): RootSyncEntry[] {\n return this._entries ?? [];\n }\n\n get links(): RootSyncLink[] {\n return this._links ?? [];\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n this._serverTime = attributes.serverTime ? new Date(attributes.serverTime) : undefined;\n this._locked = Boolean(attributes.locked);\n this._lockedFrom = attributes.lockedFrom ? new Date(attributes.lockedFrom) : undefined;\n this._entries = (attributes.entries ?? []).map((entry: { type: string; id: string; updatedAt: string }) => ({\n type: entry.type,\n id: entry.id,\n updatedAt: new Date(entry.updatedAt),\n }));\n this._links = (attributes.links ?? []).map((link: RootSyncLink) => ({\n fromType: link.fromType,\n fromId: link.fromId,\n toType: link.toType,\n toId: link.toId,\n kind: link.kind,\n props: link.props ?? {},\n }));\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"RootSync is read-only and cannot be created\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-4XLDBOSE.js","../src/core/factories/RehydrationFactory.ts","../src/core/abstracts/AbstractApiData.ts","../src/i18n/config.ts","../src/core/registry/ModuleRegistry.ts","../src/core/endpoint/EndpointCreator.ts","../src/offline/sync/models/RootSync.ts"],"names":["includedData","includedItem","module","entity"],"mappings":"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","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/chunk-4XLDBOSE.js","sourcesContent":[null,"import { ApiDataInterface } from \"../interfaces/ApiDataInterface\";\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { JsonApiHydratedDataInterface } from \"../interfaces/JsonApiHydratedDataInterface\";\nimport { DataClassRegistry } from \"../registry/DataClassRegistry\";\n\n/**\n * The resources being rehydrated right now, along the current chain of relationships. A resource\n * met again inside its own chain (A's relationship leads to B, B's back to A) is a circle in the\n * `included` data: following it would recurse until the stack overflows, so it is skipped there.\n */\nconst inProgress = new Set<string>();\n\nconst keyOf = (data: JsonApiHydratedDataInterface): string | undefined => {\n const resource = data?.jsonApi;\n return resource?.type && resource?.id ? `${resource.type}:${resource.id}` : undefined;\n};\n\nfunction guarded<T>(data: JsonApiHydratedDataInterface, run: () => T): T {\n const key = keyOf(data);\n if (!key) return run();\n if (inProgress.has(key)) return undefined as T;\n inProgress.add(key);\n try {\n return run();\n } finally {\n inProgress.delete(key);\n }\n}\n\nexport class RehydrationFactory {\n public static rehydrate<T extends ApiDataInterface>(\n classKey: ApiRequestDataTypeInterface,\n data: JsonApiHydratedDataInterface,\n ): T {\n const factoryClass = DataClassRegistry.get(classKey);\n\n return guarded(data, () => new factoryClass().rehydrate(data) as T);\n }\n\n public static rehydrateList<T extends ApiDataInterface>(\n classKey: ApiRequestDataTypeInterface,\n data: JsonApiHydratedDataInterface[],\n ): T[] {\n const factoryClass = DataClassRegistry.get(classKey);\n\n return data.map((item: JsonApiHydratedDataInterface) =>\n guarded(item, () => new factoryClass().rehydrate(item) as T),\n );\n }\n}\n","import { RehydrationFactory } from \"../factories/RehydrationFactory\";\nimport { ApiDataInterface } from \"../interfaces/ApiDataInterface\";\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { JsonApiHydratedDataInterface } from \"../interfaces/JsonApiHydratedDataInterface\";\n\nexport abstract class AbstractApiData implements ApiDataInterface {\n static identifierFields: string[] = [\"name\"];\n\n get identifier(): string {\n const fields = (this.constructor as typeof AbstractApiData).identifierFields;\n return fields\n .map((field) => this._jsonApi?.attributes?.[field])\n .filter((v) => v != null && v !== \"\")\n .join(\" \");\n }\n\n protected _jsonApi?: any;\n protected _included?: any[];\n\n protected _id?: string;\n protected _type?: string;\n protected _createdAt?: Date;\n protected _updatedAt?: Date;\n\n protected _self?: string;\n\n get type(): string {\n if (!this._type) throw new Error(\"Type is not set.\");\n return this._type;\n }\n\n get id(): string {\n if (!this._id) throw new Error(\"Id is not set.\");\n return this._id;\n }\n\n get self(): string | undefined {\n return this._self;\n }\n\n get createdAt(): Date {\n return this._createdAt ?? new Date();\n }\n\n get updatedAt(): Date {\n return this._updatedAt ?? new Date();\n }\n\n get included(): any[] {\n return this._included ?? [];\n }\n\n get jsonApi(): any {\n return this._jsonApi;\n }\n\n ingestJsonApi(_data: JsonApiHydratedDataInterface): void {}\n\n generateApiUrl(_params?: any): string {\n throw new Error(\"Method not implemented.\");\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"Method not implemented.\");\n }\n\n protected _readIncluded<T extends ApiDataInterface>(\n data: JsonApiHydratedDataInterface,\n type: string,\n dataType: ApiRequestDataTypeInterface,\n ): T | T[] | undefined {\n if (\n data.included === undefined ||\n data.included.length === 0 ||\n data.jsonApi.relationships === undefined ||\n data.jsonApi.relationships[type] === undefined ||\n data.jsonApi.relationships[type].data === undefined\n ) {\n return undefined;\n }\n\n if (Array.isArray(data.jsonApi.relationships[type].data)) {\n const response: T[] = data.jsonApi.relationships[type].data.map((jsonApiData: any) => {\n const includedData = data.included.find(\n (includedData: any) => includedData.id === jsonApiData.id && includedData.type === jsonApiData.type,\n );\n\n if (includedData === undefined) return undefined;\n\n return RehydrationFactory.rehydrate(dataType, {\n jsonApi: includedData,\n included: data.included,\n }) as T;\n });\n\n return response.filter((item: T | undefined) => item !== undefined) as T[];\n }\n\n const includedData = data.included.find(\n (includedData: any) =>\n includedData.id === data.jsonApi.relationships[type].data.id &&\n includedData.type === data.jsonApi.relationships[type].data.type,\n );\n\n if (includedData === undefined && data.allData !== undefined) {\n // Try to find in allData as a fallback\n const fallbackData = data.allData.find(\n (includedData: any) =>\n includedData.id === data.jsonApi.relationships[type].data.id &&\n includedData.type === data.jsonApi.relationships[type].data.type,\n );\n if (fallbackData !== undefined) {\n return RehydrationFactory.rehydrate(dataType, {\n jsonApi: fallbackData,\n included: data.included,\n }) as T;\n }\n }\n\n if (includedData === undefined) return undefined;\n\n return RehydrationFactory.rehydrate(dataType, {\n jsonApi: includedData,\n included: data.included,\n }) as T;\n }\n\n /**\n * Read included relationships with polymorphic type resolution.\n * Determines the correct model class based on the JSON:API type in included data.\n */\n protected _readIncludedPolymorphic<T extends ApiDataInterface>(\n data: JsonApiHydratedDataInterface,\n relationshipKey: string,\n candidateModules: ApiRequestDataTypeInterface[],\n ): T | T[] | undefined {\n // Validate relationship exists\n if (!data.included?.length || !data.jsonApi.relationships?.[relationshipKey]?.data) {\n return undefined;\n }\n\n const relData = data.jsonApi.relationships[relationshipKey].data;\n\n // Build lookup map: JSON:API type -> module\n const typeToModule = new Map<string, ApiRequestDataTypeInterface>();\n for (const mod of candidateModules) {\n typeToModule.set(mod.name, mod);\n }\n\n if (Array.isArray(relData)) {\n // Many relationship\n const result: T[] = [];\n for (const item of relData) {\n const includedItem = data.included.find((inc: any) => inc.id === item.id && inc.type === item.type);\n if (!includedItem) continue;\n\n // Resolve module by JSON:API type\n const module = typeToModule.get(includedItem.type);\n if (!module) continue;\n\n result.push(\n RehydrationFactory.rehydrate(module, {\n jsonApi: includedItem,\n included: data.included,\n }) as T,\n );\n }\n return result.length > 0 ? result : undefined;\n }\n\n // Single relationship\n const includedItem = data.included.find((inc: any) => inc.id === relData.id && inc.type === relData.type);\n if (!includedItem) return undefined;\n\n const module = typeToModule.get(includedItem.type);\n if (!module) return undefined;\n\n return RehydrationFactory.rehydrate(module, {\n jsonApi: includedItem,\n included: data.included,\n }) as T;\n }\n\n /**\n * Read included relationship data and augment with relationship meta properties.\n * Handles both single relationships (one-to-one) and array relationships (one-to-many).\n *\n * For single relationships: meta is read from `relationships[type].meta`\n * For array relationships: per-item meta is read from `relationships[type].data[].meta`\n *\n * @param data - Hydrated JSON:API data\n * @param type - Relationship type key (e.g., \"guide\", \"persons\")\n * @param dataType - Module reference for rehydration\n * @returns Related object(s) augmented with meta properties, or undefined\n */\n protected _readIncludedWithMeta<T extends ApiDataInterface, M extends Record<string, any>>(\n data: JsonApiHydratedDataInterface,\n type: string,\n dataType: ApiRequestDataTypeInterface,\n ): (T & M) | (T & M)[] | undefined {\n // Check if relationship exists\n if (\n data.included === undefined ||\n data.included.length === 0 ||\n data.jsonApi.relationships === undefined ||\n data.jsonApi.relationships[type] === undefined ||\n data.jsonApi.relationships[type].data === undefined\n ) {\n return undefined;\n }\n\n const relationshipData = data.jsonApi.relationships[type].data;\n\n // Handle array relationships with per-item meta\n if (Array.isArray(relationshipData)) {\n const result: (T & M)[] = [];\n\n for (const item of relationshipData) {\n const includedData = data.included.find((inc: any) => inc.id === item.id && inc.type === item.type);\n\n if (!includedData) continue;\n\n const entity = RehydrationFactory.rehydrate(dataType, {\n jsonApi: includedData,\n included: data.included,\n }) as T;\n\n // Merge per-item meta from relationships[type].data[].meta\n if (item.meta) {\n AbstractApiData._mergeMeta(entity, item.meta);\n }\n\n result.push(entity as T & M);\n }\n\n return result;\n }\n\n // Handle single relationship\n const includedData = data.included.find(\n (inc: any) => inc.id === relationshipData.id && inc.type === relationshipData.type,\n );\n\n if (!includedData) {\n // Try to find in allData as a fallback\n if (data.allData !== undefined) {\n const fallbackData = data.allData.find(\n (inc: any) => inc.id === relationshipData.id && inc.type === relationshipData.type,\n );\n if (fallbackData) {\n const entity = RehydrationFactory.rehydrate(dataType, {\n jsonApi: fallbackData,\n included: data.included,\n }) as T;\n\n // Merge relationship-level meta for single relationships\n const relationshipMeta = data.jsonApi.relationships[type].meta;\n if (relationshipMeta) {\n AbstractApiData._mergeMeta(entity, relationshipMeta);\n }\n return entity as T & M;\n }\n }\n return undefined;\n }\n\n const entity = RehydrationFactory.rehydrate(dataType, {\n jsonApi: includedData,\n included: data.included,\n }) as T;\n\n // Merge relationship-level meta for single relationships\n const relationshipMeta = data.jsonApi.relationships[type].meta;\n if (relationshipMeta) {\n AbstractApiData._mergeMeta(entity, relationshipMeta);\n }\n\n return entity as T & M;\n }\n\n /**\n * Copies relationship meta onto a rehydrated entity as own data properties. Object.assign throws\n * when the model declares a getter-only accessor of the same name (an app's `Chunk` with\n * `get relevance()`); an own property shadows that getter, so the model reads the meta value.\n */\n private static _mergeMeta(entity: object, meta: Record<string, any>): void {\n for (const [key, value] of Object.entries(meta)) {\n Object.defineProperty(entity, key, { value, writable: true, enumerable: true, configurable: true });\n }\n }\n\n dehydrate(): JsonApiHydratedDataInterface {\n return {\n jsonApi: this._jsonApi,\n included: this._included ?? [],\n };\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n this._jsonApi = data.jsonApi;\n this._included = data.included;\n\n this._type = this._jsonApi.type;\n this._id = this._jsonApi.id;\n this._createdAt = this._jsonApi.meta?.createdAt !== undefined ? new Date(this._jsonApi.meta.createdAt) : undefined;\n this._updatedAt = this._jsonApi.meta?.updatedAt !== undefined ? new Date(this._jsonApi.meta.updatedAt) : undefined;\n\n this._self = this._jsonApi.links?.self ?? undefined;\n\n return this;\n }\n}\n","import { ComponentType } from \"react\";\n\n// Types for injected hooks\nexport interface I18nRouter {\n push: (href: string) => void;\n replace: (href: string) => void;\n back: () => void;\n forward: () => void;\n refresh: () => void;\n prefetch: (href: string) => void;\n}\n\nexport type UseRouterHook = () => I18nRouter;\nexport type UseTranslationsHook = (namespace?: string) => (key: string, values?: Record<string, any>) => string;\nexport type UseLocaleHook = () => string;\nexport type GetLocaleFunction = () => Promise<string>;\n\nexport type UseDateFnsLocaleHook = () => any; // date-fns Locale type\nexport type LinkComponent = ComponentType<{ href: string; children: React.ReactNode; [key: string]: any }>;\n\nexport interface I18nConfig {\n useRouter: UseRouterHook;\n useTranslations: UseTranslationsHook;\n useLocale?: UseLocaleHook;\n useDateFnsLocale?: UseDateFnsLocaleHook;\n Link: LinkComponent;\n usePathname: () => string;\n /**\n * Server-side locale for API requests (pass next-intl's `getLocale` from `next-intl/server`).\n *\n * The library never imports next-intl itself: instrumentation loads the library, and\n * next-intl's `getLocale` pulls in the app's `i18n/request.ts`, which reads\n * `next/root-params` — a module only allowed inside the app directory.\n */\n getLocale: GetLocaleFunction;\n /**\n * Locales the app supports. The first entry is the fallback.\n * Defaults to `[\"en\"]` when omitted.\n */\n supportedLocales?: string[];\n}\n\n// Private storage\nlet _config: I18nConfig | null = null;\n\n// Configuration function (called by app at startup)\nexport function configureI18n(config: I18nConfig): void {\n _config = config;\n}\n\n// Hooks for library components to use\nexport function useI18nRouter(): I18nRouter {\n if (!_config?.useRouter) {\n throw new Error(\"i18n not configured. Call configureI18n() at app startup.\");\n }\n return _config.useRouter();\n}\n\nexport function useI18nTranslations(namespace?: string): (key: string, values?: Record<string, any>) => string {\n if (!_config?.useTranslations) {\n // Fallback: return key as-is (safe for server/client)\n return (key: string) => key;\n }\n return _config.useTranslations(namespace);\n}\n\nexport function getI18nLink(): LinkComponent {\n if (!_config?.Link) {\n throw new Error(\"i18n not configured. Call configureI18n() at app startup.\");\n }\n return _config.Link;\n}\n\nexport function useI18nLocale(): string {\n if (_config?.useLocale) {\n return _config.useLocale();\n }\n // Fallback to English (safe for server/client)\n return \"en\";\n}\n\n/** The server-side locale from the app's `getLocale`. Throws when `configureI18n` has not run. */\nexport async function getI18nServerLocale(): Promise<string> {\n if (!_config?.getLocale) {\n throw new Error(\"i18n not configured. Call configureI18n() with getLocale at app startup.\");\n }\n return _config.getLocale();\n}\n\nexport function useI18nDateFnsLocale(): any {\n if (_config?.useDateFnsLocale) {\n return _config.useDateFnsLocale();\n }\n // Fallback to undefined (Calendar will use default)\n return undefined;\n}\n\n/** The app's supported locales. Defaults to `[\"en\"]` when not configured. */\nexport function getI18nSupportedLocales(): string[] {\n if (_config?.supportedLocales && _config.supportedLocales.length > 0) {\n return _config.supportedLocales;\n }\n return [\"en\"];\n}\n\n/** The app's default locale: the first supported locale (`\"en\"` when not configured). */\nexport function getI18nDefaultLocale(): string {\n return getI18nSupportedLocales()[0];\n}\n","import { ModuleWithPermissions } from \"../../permissions/types\";\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\nimport { hasBootstrapper, tryBootstrap } from \"./bootstrapStore\";\n\n// Foundation module types - defined by LIBRARY\nexport interface FoundationModuleDefinitions {\n S3: ModuleWithPermissions;\n Auth: ModuleWithPermissions;\n User: ModuleWithPermissions;\n Author: ModuleWithPermissions;\n Company: ModuleWithPermissions;\n Role: ModuleWithPermissions;\n Notification: ModuleWithPermissions;\n Push: ModuleWithPermissions;\n Feature: ModuleWithPermissions;\n Module: ModuleWithPermissions;\n Content: ModuleWithPermissions;\n HowTo: ModuleWithPermissions;\n HandbookPage: ModuleWithPermissions;\n HandbookSection: ModuleWithPermissions;\n HandbookThread: ModuleWithPermissions;\n HandbookThreadMessage: ModuleWithPermissions;\n Assistant: ModuleWithPermissions;\n AssistantMessage: ModuleWithPermissions;\n AssistantAction: ModuleWithPermissions;\n Chunk: ModuleWithPermissions;\n // Billing modules - READ: all users, UPDATE: CompanyAdministrator, ADMIN: Administrator\n Billing: ModuleWithPermissions;\n StripeCustomer: ModuleWithPermissions;\n StripePaymentMethod: ModuleWithPermissions;\n StripeSubscription: ModuleWithPermissions;\n StripeInvoice: ModuleWithPermissions;\n StripeProduct: ModuleWithPermissions;\n StripePrice: ModuleWithPermissions;\n StripeUsage: ModuleWithPermissions;\n StripePromotionCode: ModuleWithPermissions;\n StripeCoupon: ModuleWithPermissions;\n StripeCouponCode: ModuleWithPermissions;\n StripeCouponOffer: ModuleWithPermissions;\n // AI connection administration\n AiConnection: ModuleWithPermissions;\n // OAuth modules\n OAuth: ModuleWithPermissions;\n // Waitlist modules\n Waitlist: ModuleWithPermissions;\n WaitlistStats: ModuleWithPermissions;\n // Email automation modules\n EmailAutomation: ModuleWithPermissions;\n EmailAutomationStep: ModuleWithPermissions;\n EmailAutomationEnrollment: ModuleWithPermissions;\n EmailAutomationTrigger: ModuleWithPermissions;\n // Email blast modules\n EmailBlast: ModuleWithPermissions;\n EmailBlastDelivery: ModuleWithPermissions;\n EmailBlastAudience: ModuleWithPermissions;\n // Blog modules\n BlogPost: ModuleWithPermissions;\n BlogPostTranslation: ModuleWithPermissions;\n BlogCategory: ModuleWithPermissions;\n BlogCategoryTranslation: ModuleWithPermissions;\n BlogSettings: ModuleWithPermissions;\n // Referral modules\n Referral: ModuleWithPermissions;\n ReferralStats: ModuleWithPermissions;\n // Referral link visits and the administrative referral report (opt-in: registered by apps that spread referralAdminModules)\n ReferralVisit: ModuleWithPermissions;\n ReferralAdminSummary: ModuleWithPermissions;\n ReferralAdminReferrer: ModuleWithPermissions;\n ReferralAdminReferral: ModuleWithPermissions;\n // Two-factor authentication modules\n TotpAuthenticator: ModuleWithPermissions;\n TotpSetup: ModuleWithPermissions;\n TotpVerify: ModuleWithPermissions;\n TotpVerifyLogin: ModuleWithPermissions;\n Passkey: ModuleWithPermissions;\n PasskeyRegistrationOptions: ModuleWithPermissions;\n PasskeyRegistrationVerify: ModuleWithPermissions;\n PasskeyRename: ModuleWithPermissions;\n PasskeyVerifyLogin: ModuleWithPermissions;\n PasskeyAuthenticationOptions: ModuleWithPermissions;\n TwoFactorEnable: ModuleWithPermissions;\n TwoFactorChallenge: ModuleWithPermissions;\n TwoFactorStatus: ModuleWithPermissions;\n BackupCodeVerify: ModuleWithPermissions;\n // OAuth sign-up awaiting consent\n PendingRegistration: ModuleWithPermissions;\n // RBAC modules\n PermissionMapping: ModuleWithPermissions;\n ModulePaths: ModuleWithPermissions;\n RbacMatrix: ModuleWithPermissions;\n // Audit modules\n AuditLog: ModuleWithPermissions;\n // Administrative token-usage reporting modules\n TokenUsageAdminSummary: ModuleWithPermissions;\n TokenUsageAdminTimeline: ModuleWithPermissions;\n TokenUsageAdminBreakdown: ModuleWithPermissions;\n // Self-service token-usage reporting modules\n TokenUsageReportSummary: ModuleWithPermissions;\n TokenUsageReportTimeline: ModuleWithPermissions;\n TokenUsageReportBreakdown: ModuleWithPermissions;\n // First-party web analytics modules (tracker + administrative dashboard)\n AnalyticsEvent: ModuleWithPermissions;\n AnalyticsSummary: ModuleWithPermissions;\n AnalyticsTimeline: ModuleWithPermissions;\n AnalyticsBreakdown: ModuleWithPermissions;\n AnalyticsSession: ModuleWithPermissions;\n AnalyticsPageView: ModuleWithPermissions;\n // Account deletion (opt-in: registered only by apps that enable it)\n AccountDeletion: ModuleWithPermissions;\n // Offline mode (opt-in: registered by apps that spread offlineModules)\n OfflineChangeSet: ModuleWithPermissions;\n RootSync: ModuleWithPermissions;\n}\n\n// App-specific modules - apps will augment this interface ONLY\n\nexport interface AppModuleDefinitions {}\n\n// Combined type for full autocompletion\nexport type ModuleDefinitions = FoundationModuleDefinitions & AppModuleDefinitions;\n\n// Symbol key to avoid conflicts with other globals\nconst MODULES_KEY = Symbol.for(\"nextjs-jsonapi:modules\");\n\n// Use globalThis to persist module registry across HMR reloads\nconst globalStore = globalThis as unknown as {\n [MODULES_KEY]?: Map<string, ApiRequestDataTypeInterface>;\n};\n\n// Initialize global modules map if not set\nif (!globalStore[MODULES_KEY]) {\n globalStore[MODULES_KEY] = new Map();\n}\n\nclass ModuleRegistryClass {\n private get _modules(): Map<string, ApiRequestDataTypeInterface> {\n return globalStore[MODULES_KEY]!;\n }\n\n register<K extends string>(name: K, module: ApiRequestDataTypeInterface): void {\n this._modules.set(name, module);\n }\n\n get<K extends keyof ModuleDefinitions>(name: K): ModuleDefinitions[K] {\n let module = this._modules.get(name as string);\n\n // Self-healing: if module not found, try bootstrapping first\n if (!module) {\n const didBootstrap = tryBootstrap();\n if (didBootstrap) {\n // Retry after bootstrap\n module = this._modules.get(name as string);\n }\n }\n\n if (!module) {\n // Provide helpful error message based on state\n const hint = hasBootstrapper()\n ? \"Bootstrap was called but module still not found. Check module registration.\"\n : \"No bootstrapper registered. Ensure configureJsonApi({ bootstrapper }) is called before accessing modules.\";\n throw new Error(`Module \"${String(name)}\" not registered. ${hint}`);\n }\n\n return module as ModuleDefinitions[K];\n }\n\n /**\n * True when a module is registered under `name`. Never throws, so a component\n * can check for an opt-in module the app may not have registered.\n */\n has(name: string): boolean {\n // Bootstrap only an empty registry (as findByName does): an app that never\n // registers `name` must not re-run its bootstrapper on every check.\n if (this._modules.size === 0) tryBootstrap();\n return this._modules.has(name);\n }\n\n findByName(moduleName: string): ModuleWithPermissions {\n // Self-healing: try bootstrap if registry is empty\n if (this._modules.size === 0) {\n tryBootstrap();\n }\n\n // Search by module's name property (e.g., \"topics\", \"articles\")\n for (const module of this._modules.values()) {\n if ((module as ModuleWithPermissions).name === moduleName) {\n return module as ModuleWithPermissions;\n }\n }\n throw new Error(`Module not found: ${moduleName}`);\n }\n\n getAllPageUrls(): { id: string; text: string }[] {\n if (this._modules.size === 0) {\n tryBootstrap();\n }\n\n const seen = new Set<string>();\n const result: { id: string; text: string }[] = [];\n for (const [key, module] of this._modules.entries()) {\n const m = module as ModuleWithPermissions;\n if (m.pageUrl && !seen.has(m.pageUrl)) {\n seen.add(m.pageUrl);\n result.push({ id: m.pageUrl, text: key });\n result.push({ id: `${m.pageUrl}/:id`, text: `${key} (detail)` });\n }\n }\n return result.sort((a, b) => a.text.localeCompare(b.text));\n }\n\n findByModelName(modelName: string): ModuleWithPermissions {\n // Direct lookup by registry key (e.g., \"Article\", \"Document\")\n let module = this._modules.get(modelName);\n\n // Self-healing: if not found, try bootstrapping\n if (!module) {\n const didBootstrap = tryBootstrap();\n if (didBootstrap) {\n module = this._modules.get(modelName);\n }\n }\n\n if (!module) {\n throw new Error(`Module not found for model: ${modelName}`);\n }\n return module as ModuleWithPermissions;\n }\n\n findByFeature(feature: string): ModuleWithPermissions[] {\n const response: ModuleWithPermissions[] = [];\n\n for (const module of this._modules.values()) {\n const m = module as ModuleWithPermissions;\n if (m.feature === feature) {\n response.push(m);\n }\n }\n\n return response;\n }\n\n getAll(): ApiRequestDataTypeInterface[] {\n if (this._modules.size === 0) {\n tryBootstrap();\n }\n return Array.from(this._modules.values());\n }\n}\n\nexport const ModuleRegistry = new ModuleRegistryClass();\n\n// Proxy object for Modules.X syntax with autocompletion\nexport const Modules = new Proxy({} as ModuleDefinitions, {\n get(_, prop: string) {\n if (prop === \"findByName\") {\n return (name: string) => ModuleRegistry.findByName(name);\n }\n if (prop === \"findByModelName\") {\n return (name: string) => ModuleRegistry.findByModelName(name);\n }\n if (prop === \"findByFeature\") {\n return (feature: string) => ModuleRegistry.findByFeature(feature);\n }\n return ModuleRegistry.get(prop as keyof ModuleDefinitions);\n },\n}) as ModuleDefinitions & {\n findByName: (name: string) => ModuleWithPermissions;\n findByModelName: (name: string) => ModuleWithPermissions;\n findByFeature: (feature: string) => ModuleWithPermissions[];\n};\n","import { FieldSelector } from \"../fields/FieldSelector\";\nimport { ApiRequestDataTypeInterface } from \"../interfaces/ApiRequestDataTypeInterface\";\n\nexport type EndpointQuery = {\n endpoint: ApiRequestDataTypeInterface | string;\n id?: string;\n childEndpoint?: ApiRequestDataTypeInterface | string;\n childId?: string;\n additionalParams?: { key: string; value: string | string[] }[];\n};\n\nexport class EndpointCreator {\n private _endpoint: EndpointQuery;\n\n constructor(params: {\n endpoint: ApiRequestDataTypeInterface | string;\n id?: string;\n childEndpoint?: ApiRequestDataTypeInterface | string;\n childId?: string;\n additionalParams?: { key: string; value: string }[];\n }) {\n this._endpoint = {\n endpoint: params.endpoint,\n id: params.id,\n childEndpoint: params.childEndpoint,\n childId: params.childId,\n additionalParams: params.additionalParams ?? [],\n };\n }\n\n endpoint(value: ApiRequestDataTypeInterface | string): EndpointCreator {\n this._endpoint.endpoint = value;\n return this;\n }\n\n id(value: string): EndpointCreator {\n this._endpoint.id = value;\n return this;\n }\n\n childEndpoint(value: ApiRequestDataTypeInterface | string): EndpointCreator {\n this._endpoint.childEndpoint = value;\n return this;\n }\n\n childId(value: string): EndpointCreator {\n this._endpoint.childId = value;\n return this;\n }\n\n set additionalParams(value: { key: string; value: string }[]) {\n this._endpoint.additionalParams = value;\n }\n\n addAdditionalParam(key: string, value: string | string[]): EndpointCreator {\n if (!this._endpoint.additionalParams) this._endpoint.additionalParams = [];\n this._endpoint.additionalParams.push({ key, value });\n return this;\n }\n\n limitToType(selectors: string[]): this {\n this.addAdditionalParam(`include`, selectors.join(\",\"));\n return this;\n }\n\n limitToFields(selectors: FieldSelector<any>[]): this {\n if (selectors.length === 0) return this;\n\n selectors.forEach((selector) => {\n const fieldString = selector.fields.join(\",\");\n this.addAdditionalParam(`fields[${selector.type}]`, fieldString);\n });\n\n return this;\n }\n\n generate(): string {\n let additionalParams = \"\";\n if (this._endpoint.additionalParams) {\n additionalParams = this._endpoint.additionalParams.map((param) => `${param.key}=${param.value}`).join(\"&\");\n }\n\n let response = ``;\n response += `${\n typeof this._endpoint.endpoint === \"string\" ? this._endpoint.endpoint : this._endpoint.endpoint.name\n }`;\n if (this._endpoint.id) response += `/${this._endpoint.id}`;\n if (this._endpoint.childEndpoint) {\n response += `/${\n typeof this._endpoint.childEndpoint === \"string\"\n ? this._endpoint.childEndpoint\n : this._endpoint.childEndpoint.name\n }`;\n }\n if (this._endpoint.childId) response += `/${this._endpoint.childId}`;\n if (additionalParams) response += `?${additionalParams}`;\n\n return response;\n }\n}\n","import { AbstractApiData } from \"../../../core/abstracts/AbstractApiData\";\nimport { JsonApiHydratedDataInterface } from \"../../../core/interfaces/JsonApiHydratedDataInterface\";\nimport { RootSyncEntry, RootSyncInterface, RootSyncLink } from \"./RootSyncInterface\";\n\n/**\n * Child path of the sync answer under the root module record\n * (`GET /<root endpoint>/:id/sync`), the same value as the app API's sync meta endpoint\n * (in Wyrdli, `campaignSyncMeta.endpoint`).\n */\nexport const ROOT_SYNC_ENDPOINT = \"sync\";\n\n/** The sync answer for one root record (`root-syncs`, id = root record id). Read-only. */\nexport class RootSync extends AbstractApiData implements RootSyncInterface {\n private _serverTime?: Date;\n private _locked: boolean = false;\n private _lockedFrom?: Date;\n private _entries?: RootSyncEntry[];\n private _links?: RootSyncLink[];\n\n get serverTime(): Date {\n if (!this._serverTime) throw new Error(\"serverTime is not set.\");\n return this._serverTime;\n }\n\n get locked(): boolean {\n return this._locked;\n }\n\n get lockedFrom(): Date | undefined {\n return this._lockedFrom;\n }\n\n get entries(): RootSyncEntry[] {\n return this._entries ?? [];\n }\n\n get links(): RootSyncLink[] {\n return this._links ?? [];\n }\n\n rehydrate(data: JsonApiHydratedDataInterface): this {\n super.rehydrate(data);\n\n const attributes = data.jsonApi.attributes ?? {};\n this._serverTime = attributes.serverTime ? new Date(attributes.serverTime) : undefined;\n this._locked = Boolean(attributes.locked);\n this._lockedFrom = attributes.lockedFrom ? new Date(attributes.lockedFrom) : undefined;\n this._entries = (attributes.entries ?? []).map((entry: { type: string; id: string; updatedAt: string }) => ({\n type: entry.type,\n id: entry.id,\n updatedAt: new Date(entry.updatedAt),\n }));\n this._links = (attributes.links ?? []).map((link: RootSyncLink) => ({\n fromType: link.fromType,\n fromId: link.fromId,\n toType: link.toType,\n toId: link.toId,\n kind: link.kind,\n props: link.props ?? {},\n }));\n\n return this;\n }\n\n createJsonApi(_data?: any): any {\n throw new Error(\"RootSync is read-only and cannot be created\");\n }\n}\n"]}
|
|
@@ -225,6 +225,10 @@ function refreshOfflineDeviceState() {
|
|
|
225
225
|
});
|
|
226
226
|
}
|
|
227
227
|
__name(refreshOfflineDeviceState, "refreshOfflineDeviceState");
|
|
228
|
+
function rootKinds(config) {
|
|
229
|
+
return [{ module: config.rootModule, listSyncRoots: config.listSyncRoots }, ...config.extraRootKinds ?? []];
|
|
230
|
+
}
|
|
231
|
+
__name(rootKinds, "rootKinds");
|
|
228
232
|
function getOfflineConfig() {
|
|
229
233
|
if (!_config) throw new Error("Offline mode is not configured. Call configureOffline() first.");
|
|
230
234
|
return _config;
|
|
@@ -306,6 +310,7 @@ export {
|
|
|
306
310
|
setOfflineEnabledOnDevice,
|
|
307
311
|
writeOfflineLastSync,
|
|
308
312
|
refreshOfflineDeviceState,
|
|
313
|
+
rootKinds,
|
|
309
314
|
getOfflineConfig,
|
|
310
315
|
setDefaultDeviceStoreOpener,
|
|
311
316
|
getSqliteModuleUrl,
|
|
@@ -314,4 +319,4 @@ export {
|
|
|
314
319
|
getOfflineStore,
|
|
315
320
|
forgetOfflineStore
|
|
316
321
|
};
|
|
317
|
-
//# sourceMappingURL=chunk-
|
|
322
|
+
//# sourceMappingURL=chunk-4ZJIUTIV.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/offline/state.ts","../src/offline/types.ts","../src/offline/config.ts"],"sourcesContent":["// Kept free of React: `directFetch` (every entry, server ones included) imports this module.\n// The hooks live in state-hooks.ts, exported only from the `./offline` entry.\n\nexport type OfflineState = {\n online: boolean;\n pending: number;\n conflicts: number;\n draining: boolean;\n lockedRootIds: string[];\n /**\n * False once this browser was found unable to keep the device copy (no secure context, no\n * private file storage, or the worker reports the OPFS APIs missing). Offline mode is then off\n * for the rest of the page: `isOfflineConfigured()` answers false and nothing offline runs.\n */\n available: boolean;\n /**\n * Offline mode is on on this device for the signed-in user. Always true once configured\n * without `OfflineConfig.requireOptIn`; otherwise the GM's per-device switch.\n */\n enabled: boolean;\n /** The running full sync: root records done out of the total, null when no sync runs. */\n syncProgress: { done: number; total: number } | null;\n /** When the last full sync on this device finished (epoch ms), null when never. */\n lastSyncedAt: number | null;\n /** How many root records the last full sync kept on this device, null when unknown. */\n rootCount: number | null;\n};\n\nexport const SERVER_SNAPSHOT: OfflineState = {\n online: true,\n pending: 0,\n conflicts: 0,\n draining: false,\n lockedRootIds: [],\n available: true,\n enabled: false,\n syncProgress: null,\n lastSyncedAt: null,\n rootCount: null,\n};\n\nlet _state: OfflineState = { ...SERVER_SNAPSHOT };\nconst _listeners = new Set<(s: OfflineState) => void>();\nlet _detachBrowserEvents: (() => void) | null = null;\n\nexport function getOfflineState(): OfflineState {\n return _state;\n}\n\nexport function subscribeOfflineState(listener: (s: OfflineState) => void): () => void {\n _listeners.add(listener);\n return () => {\n _listeners.delete(listener);\n };\n}\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((value, index) => value === b[index]);\n }\n return a === b;\n}\n\n// A reconnect attempt: a probe reached the API while the session is offline. The session stays\n// offline (spec § 4: offline \"until a sync succeeds\") while the drain and the sync run; their\n// reads and the change-set request go to the network meanwhile (`reachesNetworkWhileOffline`).\n// Any network failure (`online: false` set again) ends the attempt.\nlet _reconnecting = false;\nconst _passThroughUrls = new Set<string>();\n\n/** Whether a reconnect attempt (drain then sync, after a probe reached the API) is running. */\nexport function isReconnecting(): boolean {\n return _reconnecting;\n}\n\n/** Starts or ends a reconnect attempt (the offline runtime). */\nexport function setReconnecting(value: boolean): void {\n _reconnecting = value;\n if (!value) _passThroughUrls.clear();\n}\n\n/** Lets one write (the drain's change-set request) reach the network during a reconnect attempt. */\nexport function allowWhileReconnecting(url: string): () => void {\n _passThroughUrls.add(url);\n return () => {\n _passThroughUrls.delete(url);\n };\n}\n\n/**\n * While the session is offline, whether a request still goes to the network: only during a\n * reconnect attempt, for a GET or a write the attempt let through. Any other write stays local\n * (queued), so it is sent after the waiting changes, in order.\n */\nexport function reachesNetworkWhileOffline(method: string, url: string): boolean {\n if (!_reconnecting) return false;\n return method.toUpperCase() === \"GET\" || _passThroughUrls.has(url);\n}\n\n/** The sync and the drain may talk to the API: online, or during a reconnect attempt. */\nexport function canReachApi(): boolean {\n return _state.online || _reconnecting;\n}\n\n/** Applies a partial change. Listeners run once, and only when a value actually changed. */\nexport function setOfflineState(patch: Partial<OfflineState>): void {\n if (patch.online === false) setReconnecting(false);\n const changed = (Object.keys(patch) as (keyof OfflineState)[]).some(\n (key) => patch[key] !== undefined && !sameValue(patch[key], _state[key]),\n );\n if (!changed) return;\n\n _state = { ..._state, ...patch };\n for (const listener of [..._listeners]) listener(_state);\n}\n\n/**\n * Follows the browser's offline event. Called by `configureOffline`, never at import, so an app\n * that does not opt in gets no listener. The browser's online event alone never brings the\n * session back online (spec § 4: offline \"until a sync succeeds\"): the runtime probes the API on\n * that event and goes online only when the probe reaches it.\n */\nexport function attachBrowserOnlineEvents(): void {\n if (_detachBrowserEvents || typeof window === \"undefined\") return;\n\n if (typeof navigator !== \"undefined\" && typeof navigator.onLine === \"boolean\") {\n setOfflineState({ online: navigator.onLine });\n }\n\n const onOffline = () => setOfflineState({ online: false });\n window.addEventListener(\"offline\", onOffline);\n _detachBrowserEvents = () => {\n window.removeEventListener(\"offline\", onOffline);\n };\n}\n\n/** Test helper: back to the initial state, browser events detached, listeners kept. */\nexport function resetOfflineState(): void {\n _detachBrowserEvents?.();\n _detachBrowserEvents = null;\n setReconnecting(false);\n _state = { ...SERVER_SNAPSHOT };\n}\n","import type { ApiRequestDataTypeInterface } from \"../core/interfaces/ApiRequestDataTypeInterface\";\nimport type { DeviceStore, StoredRecord } from \"./store/DeviceStore\";\n\n/** Header of a request the server runs as an offline replay. Never answered locally. */\nexport const OFFLINE_REPLAY_HEADER = \"x-offline-replay\";\n\nexport type OfflineMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/** A Module (matched by its endpoint), a \":param\" capture, or a literal segment (\"playing\", \"merge\"). */\nexport type PatternSegment = ApiRequestDataTypeInterface | string;\n\n/** `url` (beyond the contract, optional): the absolute URL the request was made to, for the `links` of a list. */\nexport type OfflineRequest = { method: OfflineMethod; path: string; query: URLSearchParams; body?: any; url?: string };\n\n/** `body` is a JSON:API document, `undefined` for a 204. */\nexport type OfflineAnswer = { status: number; body: any };\n\nexport type LocalAnswerContext = { request: OfflineRequest; params: Record<string, string>; store: DeviceStore };\n\nexport type LocalAnswer = (ctx: LocalAnswerContext) => Promise<OfflineAnswer>;\n\nexport type OfflineRoute = { method: OfflineMethod; pattern: PatternSegment[]; answer: LocalAnswer; queue: boolean };\n\n/**\n * A type the API reads only as a list per target record: each changed record's target is the\n * other end of its sync link of `linkKind` (from the record to the target), and `endpoint`\n * builds that target's list path with `EndpointCreator`.\n */\nexport type OfflineLinkedListFetch = {\n linkKind: string;\n endpoint: (rootId: string, target: { type: string; id: string }) => string;\n};\n\n/** One more kind of root record beside `rootModule` (e.g. a notebook). Optional. */\nexport type OfflineRootKind = {\n /** The module of the root records of this kind. Sync reads `{module}/:rootId/sync`. */\n module: ApiRequestDataTypeInterface;\n /** The ids of the roots of this kind to keep on the device. Throw to keep the device copy unchanged. */\n listSyncRoots: () => Promise<string[]>;\n /**\n * Per record type, the full list endpoint (query string included) that downloads every record of\n * that type under one root of this kind, built from the stored root record. Replaces the type's\n * `fetch` mode under this kind only. Never given `updatedSince`.\n */\n typeEndpoints?: {\n module: ApiRequestDataTypeInterface;\n endpoint: (root: { id: string; resource: StoredRecord[\"resource\"] }) => string;\n }[];\n /** The module whose model reads this kind's sync answer. Default: the library's `RootSync` module (`root-syncs`). */\n syncModule?: ApiRequestDataTypeInterface;\n};\n\ntype LocalWriteBase = { store: DeviceStore; module: ApiRequestDataTypeInterface; id: string }; // store = the write's transaction\n\n/** What a generic local write (`afterLocalWrite`) did, inside its transaction. */\nexport type OfflineLocalWriteContext =\n | (LocalWriteBase & { method: \"POST\"; resource: StoredRecord[\"resource\"]; previous: null })\n | (LocalWriteBase & {\n method: \"PUT\" | \"PATCH\";\n resource: StoredRecord[\"resource\"];\n previous: StoredRecord[\"resource\"];\n })\n | (LocalWriteBase & { method: \"DELETE\"; resource: null; previous: StoredRecord[\"resource\"] });\n\nexport type OfflineTypeConfig = {\n module: ApiRequestDataTypeInterface;\n /**\n * How the sync fetches the type's changed records: the root record's list (`root-list`), one\n * GET per record (`by-id`), or (`linked-list`) the list of each record's target, found through\n * the sync answer's links of `linkedList.linkKind` (e.g. player notes, read per target only).\n */\n fetch: \"root-list\" | \"by-id\" | \"linked-list\";\n /** Required with `fetch: \"linked-list\"`. */\n linkedList?: OfflineLinkedListFetch;\n readOnly: boolean;\n serverOwnedFields: string[];\n /**\n * Relationship name → Neo4j relationship type (`faction` → `MEMBER_OF`). A link the device\n * writes from a request's relationships carries the mapped type, the kind synced links carry;\n * an unmapped name is stored as the name itself.\n */\n relationshipKinds?: Record<string, string>;\n /** The string attributes the server's fulltext index covers (default: every string attribute). */\n searchFields?: string[];\n /** The server's order with no `orderBy` (default `updatedAt DESC`). */\n defaultOrderBy?: string;\n /**\n * Called by the generic POST/PUT/PATCH/DELETE local answers inside the same transaction as the\n * write and the enqueue (a throw rolls both back). Never called by sync or write-through.\n */\n afterLocalWrite?: (ctx: OfflineLocalWriteContext) => Promise<void>;\n};\n\nexport type OfflineConfig = {\n getUserId: () => string | undefined;\n /**\n * Offline mode is off on each device until the user turns it on (`turnOnOffline`), stored per\n * user per device. Default (undefined/false): always on, every device keeps the copy.\n */\n requireOptIn?: boolean;\n /** The module whose records own every other record (in Wyrdli, Modules.Campaign). */\n rootModule: ApiRequestDataTypeInterface;\n types: OfflineTypeConfig[];\n routes: OfflineRoute[];\n /** Ids of the root records to keep on the device. */\n listSyncRoots: () => Promise<string[]>;\n /** More kinds of root records beside `rootModule`, each synced and kept on its own (optional). */\n extraRootKinds?: OfflineRootKind[];\n shellPath: (locale: string) => string;\n syncIntervalMs: number;\n /**\n * Where the device worker loads SQLite from at run time (default `/offline-sqlite/index.mjs`).\n * SQLite is never bundled: its own helper worker uses a computed URL that app bundlers refuse.\n * The app serves the files of `dist/offline/sqlite/` at this URL's folder.\n */\n sqliteModuleUrl?: string;\n /**\n * Where the device-copy worker is started from (default `/offline-sqlite/device-worker.mjs`).\n * It is a standalone file in `dist/offline/sqlite/`, never bundled by the app.\n */\n deviceWorkerUrl?: string;\n};\n\nexport const DEFAULT_SQLITE_MODULE_URL = \"/offline-sqlite/index.mjs\";\nexport const DEVICE_WORKER_FILE = \"device-worker.mjs\";\nexport const DEFAULT_DEVICE_WORKER_URL = `/offline-sqlite/${DEVICE_WORKER_FILE}`;\n\n/**\n * The files of `dist/offline/sqlite/` the app serves: the device worker and the SQLite files\n * copied from @sqlite.org/sqlite-wasm.\n */\nexport const OFFLINE_SQLITE_FILES = [\n DEVICE_WORKER_FILE,\n \"index.mjs\",\n \"sqlite3.wasm\",\n \"sqlite3-opfs-async-proxy.js\",\n] as const;\n\n/** Thrown by a local answer for something that only the server can do. Answered as a 503 \"needs-connection\". */\nexport class NeedsConnectionError extends Error {\n readonly status = 503;\n readonly code = \"needs-connection\";\n\n constructor(message: string = \"Needs a connection\") {\n super(message);\n this.name = \"NeedsConnectionError\";\n }\n}\n\n/** Segments joined with \"/\", modules by their endpoint. */\nexport function patternToString(pattern: PatternSegment[]): string {\n return pattern.map((segment) => (typeof segment === \"string\" ? segment : segment.name)).join(\"/\");\n}\n","import { attachBrowserOnlineEvents, getOfflineState, setOfflineState } from \"./state\";\nimport type { DeviceStore } from \"./store/DeviceStore\";\nimport {\n DEFAULT_DEVICE_WORKER_URL,\n DEFAULT_SQLITE_MODULE_URL,\n NeedsConnectionError,\n type OfflineConfig,\n type OfflineRootKind,\n} from \"./types\";\n\n// Kept free of store, worker and React imports: `directFetch` reads `isOfflineConfigured()` on\n// every request, so this module is the only offline code an app that never opts in loads.\n\nlet _config: OfflineConfig | null = null;\nlet _opener: ((userId: string) => Promise<DeviceStore>) | null = null;\n// Registered by the `./offline` entry (configure.ts), so the store code is reachable only from it.\nlet _defaultOpener: ((userId: string) => Promise<DeviceStore>) | null = null;\nconst _stores = new Map<string, Promise<DeviceStore>>();\n// Users whose device store must not be opened again (logged out) until they sign in again.\nconst _blockedUsers = new Set<string>();\n// Per user: whether offline mode is on on this device (`requireOptIn`). Read from localStorage\n// once, since `isOfflineConfigured()` runs on every request.\nconst _enabledCache = new Map<string, boolean>();\n\nconst ENABLED_KEY_PREFIX = \"nja-offline-enabled:\";\nconst LAST_SYNC_KEY_PREFIX = \"nja-offline-last-sync:\";\n\nfunction readStorage(key: string): string | null {\n try {\n return typeof localStorage === \"undefined\" ? null : localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStorage(key: string, value: string | null): void {\n try {\n if (typeof localStorage === \"undefined\") return;\n if (value === null) localStorage.removeItem(key);\n else localStorage.setItem(key, value);\n } catch {\n // Storage can be blocked or full: the switch then lasts for this page only.\n }\n}\n\n/**\n * The signed-in user's device store cannot be used: it could not be opened (opfs-sahpool holds\n * one connection per origin, so a second tab cannot open it) or the user logged out. A request\n * answered from the device copy then needs a connection (503 \"needs-connection\").\n */\nexport class DeviceStoreUnavailableError extends NeedsConnectionError {\n constructor(message: string = \"The device copy is not available in this tab\") {\n super(message);\n this.name = \"DeviceStoreUnavailableError\";\n }\n}\n\n/**\n * This browser cannot keep the device copy at all: not a secure context (https or `localhost`),\n * no private file storage (`navigator.storage.getDirectory`), or the worker reports the OPFS\n * APIs missing. Offline mode switches off for the page (`markOfflineUnavailable`). A failure on\n * a browser that can keep the copy (the file held by another tab) is a `DeviceStoreUnavailableError`.\n */\nexport class OfflineUnavailableError extends DeviceStoreUnavailableError {\n constructor(reason: string) {\n super(`Offline mode isn't available in this browser: ${reason}`);\n this.name = \"OfflineUnavailableError\";\n }\n}\n\n/**\n * Switches offline mode off for this page, once: one console warning, `available: false` in the\n * offline state. From then on `isOfflineConfigured()` answers false, so no request, sync, drain,\n * write-through or lock check touches the device copy again.\n */\nexport function markOfflineUnavailable(error: OfflineUnavailableError): void {\n if (!getOfflineState().available) return;\n console.warn(`[offline] ${error.message}. Offline mode is off in this browser.`);\n setOfflineState({ available: false });\n}\n\n/** Whether this browser can keep the device copy (true until found otherwise). */\nexport function isOfflineAvailable(): boolean {\n return getOfflineState().available;\n}\n\n/** Logout: the user's store is never opened again in this page until `unblockOfflineStore`. */\nexport function blockOfflineStore(userId: string): void {\n _blockedUsers.add(userId);\n _stores.delete(userId);\n}\n\n/** A sign-in (`onUserSignedIn`): the user's store may be opened again. */\nexport function unblockOfflineStore(userId: string): void {\n _blockedUsers.delete(userId);\n}\n\nexport function isOfflineStoreBlocked(userId: string): boolean {\n return _blockedUsers.has(userId);\n}\n\n/** Opts the app into offline mode. Without this call nothing offline runs. */\nexport function configureOffline(config: OfflineConfig): void {\n _config = config;\n attachBrowserOnlineEvents();\n refreshOfflineDeviceState();\n}\n\n/**\n * Configured, the browser can keep the device copy (see `markOfflineUnavailable`), and offline\n * mode is on on this device for the signed-in user (see `requireOptIn`).\n */\nexport function isOfflineConfigured(): boolean {\n return _config !== null && isOfflineAvailable() && isOfflineEnabledOnDevice(_config.getUserId());\n}\n\n/**\n * Whether offline mode is on on this device for `userId`. Always true without\n * `OfflineConfig.requireOptIn`; otherwise the stored switch, false with no user.\n */\nexport function isOfflineEnabledOnDevice(userId?: string | null): boolean {\n if (!_config?.requireOptIn) return true;\n if (!userId) return false;\n let enabled = _enabledCache.get(userId);\n if (enabled === undefined) {\n enabled = readStorage(ENABLED_KEY_PREFIX + userId) === \"1\";\n _enabledCache.set(userId, enabled);\n }\n return enabled;\n}\n\n/** Stores the device switch for `userId` (`turnOnOffline` / `turnOffOffline`). */\nexport function setOfflineEnabledOnDevice(userId: string, enabled: boolean): void {\n writeStorage(ENABLED_KEY_PREFIX + userId, enabled ? \"1\" : null);\n _enabledCache.set(userId, enabled);\n}\n\n/** When `userId`'s last full sync on this device finished (epoch ms), or null. */\nexport function readOfflineLastSync(userId: string): number | null {\n const value = Number(readStorage(LAST_SYNC_KEY_PREFIX + userId));\n return Number.isFinite(value) && value > 0 ? value : null;\n}\n\n/** Stores, or with null removes, when `userId`'s last full sync finished. */\nexport function writeOfflineLastSync(userId: string, at: number | null): void {\n writeStorage(LAST_SYNC_KEY_PREFIX + userId, at === null ? null : String(at));\n}\n\n/**\n * Reads the device switch and the last sync time of the signed-in user into the offline state\n * (`enabled`, `lastSyncedAt`). Does nothing before `configureOffline`.\n */\nexport function refreshOfflineDeviceState(): void {\n if (!_config) return;\n const userId = _config.getUserId();\n if (userId) _enabledCache.delete(userId);\n setOfflineState({\n enabled: isOfflineEnabledOnDevice(userId),\n lastSyncedAt: userId ? readOfflineLastSync(userId) : null,\n });\n}\n\n/**\n * Every kind of root record: the primary one (`rootModule` + `listSyncRoots`) first, then\n * `extraRootKinds`. Library-internal (not exported from the entry).\n */\nexport function rootKinds(config: OfflineConfig): OfflineRootKind[] {\n return [{ module: config.rootModule, listSyncRoots: config.listSyncRoots }, ...(config.extraRootKinds ?? [])];\n}\n\nexport function getOfflineConfig(): OfflineConfig {\n if (!_config) throw new Error(\"Offline mode is not configured. Call configureOffline() first.\");\n return _config;\n}\n\n/** The browser opener, set by the `./offline` entry's `configureOffline`. */\nexport function setDefaultDeviceStoreOpener(opener: (userId: string) => Promise<DeviceStore>): void {\n _defaultOpener = opener;\n}\n\n/** The URL the device worker loads SQLite from (`OfflineConfig.sqliteModuleUrl`). */\nexport function getSqliteModuleUrl(): string {\n return _config?.sqliteModuleUrl ?? DEFAULT_SQLITE_MODULE_URL;\n}\n\n/** The URL the device worker is started from (`OfflineConfig.deviceWorkerUrl`). */\nexport function getDeviceWorkerUrl(): string {\n return _config?.deviceWorkerUrl ?? DEFAULT_DEVICE_WORKER_URL;\n}\n\n/** Replaces how a user's device store is opened (tests pass `openMemoryDeviceStore`). */\nexport function setDeviceStoreOpener(opener: ((userId: string) => Promise<DeviceStore>) | null): void {\n _opener = opener;\n _stores.clear();\n}\n\n/**\n * The signed-in user's device store, opened once per user id. Only the signed-in user's file is\n * ever opened; with no signed-in user the request needs a connection.\n */\nexport async function getOfflineStore(): Promise<DeviceStore> {\n const userId = getOfflineConfig().getUserId();\n if (!userId) throw new NeedsConnectionError(\"No signed-in user\");\n if (!isOfflineEnabledOnDevice(userId)) throw new DeviceStoreUnavailableError(\"Offline mode is off on this device\");\n if (_blockedUsers.has(userId)) throw new DeviceStoreUnavailableError(\"The device copy was deleted at logout\");\n if (!isOfflineAvailable()) throw new OfflineUnavailableError(\"it was found unable to keep the device copy\");\n\n let store = _stores.get(userId);\n if (!store) {\n const opener = _opener ?? _defaultOpener;\n if (!opener) throw new Error(\"No device store opener: configure offline mode through the ./offline entry.\");\n store = opener(userId).catch((error) => {\n if (error instanceof OfflineUnavailableError) markOfflineUnavailable(error);\n if (error instanceof NeedsConnectionError) throw error;\n console.warn(\"[offline] the device copy could not be opened\", error);\n throw new DeviceStoreUnavailableError(\n `The device copy could not be opened: ${String((error as Error)?.message ?? error)}`,\n );\n });\n _stores.set(userId, store);\n store.catch(() => _stores.delete(userId));\n }\n return store;\n}\n\n/** Forgets the opened store of a user (logout, deleted file). */\nexport function forgetOfflineStore(userId: string): void {\n _stores.delete(userId);\n}\n\n/** Test helper: back to \"not configured\". */\nexport function resetOfflineConfig(): void {\n _config = null;\n _opener = null;\n _stores.clear();\n _blockedUsers.clear();\n _enabledCache.clear();\n}\n"],"mappings":"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","names":[]}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
var
|
|
12
|
+
var _chunkWUNSG3INjs = require('./chunk-WUNSG3IN.js');
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
var _chunk7QVYU63Ejs = require('./chunk-7QVYU63E.js');
|
|
@@ -826,7 +826,7 @@ var missingDeviceStorage = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0,
|
|
|
826
826
|
}, "missingDeviceStorage");
|
|
827
827
|
var assertDeviceStorage = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => {
|
|
828
828
|
const reason = missingDeviceStorage();
|
|
829
|
-
if (reason) throw new (0,
|
|
829
|
+
if (reason) throw new (0, _chunkWUNSG3INjs.OfflineUnavailableError)(reason);
|
|
830
830
|
}, "assertDeviceStorage");
|
|
831
831
|
var OPFS_MISSING = /Missing required OPFS APIs/i;
|
|
832
832
|
var defaultBackend = {
|
|
@@ -836,7 +836,7 @@ var defaultBackend = {
|
|
|
836
836
|
return await navigator.storage.getDirectory();
|
|
837
837
|
} catch (error) {
|
|
838
838
|
if (_optionalChain([error, 'optionalAccess', _24 => _24.name]) === "SecurityError") {
|
|
839
|
-
throw new (0,
|
|
839
|
+
throw new (0, _chunkWUNSG3INjs.OfflineUnavailableError)("the browser refused its private file storage");
|
|
840
840
|
}
|
|
841
841
|
throw error;
|
|
842
842
|
}
|
|
@@ -844,15 +844,15 @@ var defaultBackend = {
|
|
|
844
844
|
openDatabase: /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, async (userId, fileName) => {
|
|
845
845
|
assertDeviceStorage();
|
|
846
846
|
const openLocal = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, () => openWorkerDatabase(userId, fileName, {
|
|
847
|
-
workerUrl:
|
|
848
|
-
sqliteModuleUrl:
|
|
847
|
+
workerUrl: _chunkWUNSG3INjs.getDeviceWorkerUrl.call(void 0, ),
|
|
848
|
+
sqliteModuleUrl: _chunkWUNSG3INjs.getSqliteModuleUrl.call(void 0, )
|
|
849
849
|
}), "openLocal");
|
|
850
850
|
try {
|
|
851
851
|
if (!canShareDatabase()) return await openLocal();
|
|
852
852
|
return await openSharedDatabase(userId, openLocal, () => forgetClosedStore(userId));
|
|
853
853
|
} catch (error) {
|
|
854
854
|
if (OPFS_MISSING.test(String(_nullishCoalesce(_optionalChain([error, 'optionalAccess', _25 => _25.message]), () => ( error))))) {
|
|
855
|
-
throw new (0,
|
|
855
|
+
throw new (0, _chunkWUNSG3INjs.OfflineUnavailableError)("the browser lacks the file access the device copy needs (OPFS)");
|
|
856
856
|
}
|
|
857
857
|
throw error;
|
|
858
858
|
}
|
|
@@ -885,12 +885,12 @@ var entriesOf = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, async (dire
|
|
|
885
885
|
return names;
|
|
886
886
|
}, "entriesOf");
|
|
887
887
|
var deviceDirectory = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, async () => {
|
|
888
|
-
if (!
|
|
888
|
+
if (!_chunkWUNSG3INjs.isOfflineAvailable.call(void 0, )) return void 0;
|
|
889
889
|
try {
|
|
890
890
|
return await backend.getDirectory();
|
|
891
891
|
} catch (error) {
|
|
892
|
-
if (!(error instanceof
|
|
893
|
-
|
|
892
|
+
if (!(error instanceof _chunkWUNSG3INjs.OfflineUnavailableError)) throw error;
|
|
893
|
+
_chunkWUNSG3INjs.markOfflineUnavailable.call(void 0, error);
|
|
894
894
|
return void 0;
|
|
895
895
|
}
|
|
896
896
|
}, "deviceDirectory");
|
|
@@ -906,16 +906,16 @@ var closeCurrent = /* @__PURE__ */ _chunk7QVYU63Ejs.__name.call(void 0, async ()
|
|
|
906
906
|
}, "closeCurrent");
|
|
907
907
|
function openDeviceStore(userId) {
|
|
908
908
|
if (!userId) return Promise.reject(new Error("openDeviceStore needs the signed-in user id."));
|
|
909
|
-
if (
|
|
910
|
-
return Promise.reject(new (0,
|
|
909
|
+
if (_chunkWUNSG3INjs.isOfflineStoreBlocked.call(void 0, userId)) {
|
|
910
|
+
return Promise.reject(new (0, _chunkWUNSG3INjs.DeviceStoreUnavailableError)("The device copy was deleted at logout"));
|
|
911
911
|
}
|
|
912
|
-
if (!
|
|
913
|
-
return Promise.reject(new (0,
|
|
912
|
+
if (!_chunkWUNSG3INjs.isOfflineAvailable.call(void 0, )) {
|
|
913
|
+
return Promise.reject(new (0, _chunkWUNSG3INjs.OfflineUnavailableError)("it was found unable to keep the device copy"));
|
|
914
914
|
}
|
|
915
915
|
if (_optionalChain([current, 'optionalAccess', _35 => _35.userId]) === userId) return current.store;
|
|
916
916
|
const previous = closeCurrent();
|
|
917
917
|
const database = previous.then(() => backend.openDatabase(userId, deviceFileName(userId))).catch((error) => {
|
|
918
|
-
if (error instanceof
|
|
918
|
+
if (error instanceof _chunkWUNSG3INjs.OfflineUnavailableError) _chunkWUNSG3INjs.markOfflineUnavailable.call(void 0, error);
|
|
919
919
|
throw error;
|
|
920
920
|
});
|
|
921
921
|
const store = database.then(async (opened) => {
|
|
@@ -972,8 +972,8 @@ async function openMemoryDeviceStore() {
|
|
|
972
972
|
_chunk7QVYU63Ejs.__name.call(void 0, openMemoryDeviceStore, "openMemoryDeviceStore");
|
|
973
973
|
function forgetClosedStore(userId) {
|
|
974
974
|
if (_optionalChain([current, 'optionalAccess', _36 => _36.userId]) === userId) current = void 0;
|
|
975
|
-
|
|
976
|
-
|
|
975
|
+
_chunkWUNSG3INjs.forgetOfflineStore.call(void 0, userId);
|
|
976
|
+
_chunkWUNSG3INjs.refreshOfflineDeviceState.call(void 0, );
|
|
977
977
|
}
|
|
978
978
|
_chunk7QVYU63Ejs.__name.call(void 0, forgetClosedStore, "forgetClosedStore");
|
|
979
979
|
async function deleteDeviceStore(userId) {
|
|
@@ -1001,7 +1001,7 @@ async function hasUnsentChanges(userId) {
|
|
|
1001
1001
|
}
|
|
1002
1002
|
_chunk7QVYU63Ejs.__name.call(void 0, hasUnsentChanges, "hasUnsentChanges");
|
|
1003
1003
|
async function onUserSignedIn(userId) {
|
|
1004
|
-
|
|
1004
|
+
_chunkWUNSG3INjs.unblockOfflineStore.call(void 0, userId);
|
|
1005
1005
|
if (current && current.userId !== userId) await closeCurrent();
|
|
1006
1006
|
const directory = await deviceDirectory();
|
|
1007
1007
|
if (!directory) return;
|
|
@@ -1046,4 +1046,4 @@ _chunk7QVYU63Ejs.__name.call(void 0, isRootLocked, "isRootLocked");
|
|
|
1046
1046
|
|
|
1047
1047
|
|
|
1048
1048
|
exports.imageUrlOf = imageUrlOf; exports.cacheArtworkImage = cacheArtworkImage; exports.removeArtworkImage = removeArtworkImage; exports.deleteImagesCaches = deleteImagesCaches; exports.setDeviceStoreBackend = setDeviceStoreBackend; exports.openDeviceStore = openDeviceStore; exports.openMemoryDatabase = openMemoryDatabase; exports.openMemoryDeviceStore = openMemoryDeviceStore; exports.deleteDeviceStore = deleteDeviceStore; exports.hasUnsentChanges = hasUnsentChanges; exports.onUserSignedIn = onUserSignedIn; exports.isRootLocked = isRootLocked;
|
|
1049
|
-
//# sourceMappingURL=chunk-
|
|
1049
|
+
//# sourceMappingURL=chunk-6RCZ64BQ.js.map
|