@antelopejs/dms-frontend 0.9.1 → 0.9.3
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/README.md +9 -1
- package/dist/cancellation.js +2 -1
- package/dist/config.js +11 -7
- package/dist/derived-outputs.js +6 -113
- package/dist/dev-modules.js +3 -2
- package/dist/discovery.js +19 -12
- package/dist/fs-sync.js +1 -2
- package/dist/internal/guards.d.ts +5 -0
- package/dist/internal/guards.js +22 -0
- package/dist/internal/locale-catalogs.d.ts +6 -0
- package/dist/internal/locale-catalogs.js +115 -0
- package/dist/layer-aliases.js +2 -1
- package/dist/layers.js +2 -1
- package/dist/locale-files.js +9 -4
- package/dist/manifest-secrets.js +11 -4
- package/dist/manifest.js +11 -5
- package/dist/materialize.js +6 -5
- package/dist/module-set.js +2 -4
- package/dist/ports.js +8 -1
- package/dist/private-values.js +26 -17
- package/dist/project-modules.js +17 -11
- package/dist/renderer-range.js +2 -1
- package/dist/server-process.js +17 -16
- package/dist/update-check.js +23 -16
- package/dist/verify-source-runner.js +2 -2
- package/dist/workspace-setup.js +1 -2
- package/dist/workspace.js +11 -10
- package/package.json +7 -4
- package/skills/dms-frontend/SKILL.md +1 -1
- package/skills/dms-frontend/cases.json +77 -0
- package/templates/vue/app-runtime.ts +88 -7
- package/templates/vue/client-chunking.mjs +0 -5
- package/templates/vue/client-server-guard.mjs +10 -13
- package/templates/vue/entry-weight.mjs +12 -14
- package/templates/vue/frontend-module.ts +159 -3563
- package/templates/vue/frontend-runtime/async-data-entry.ts +320 -0
- package/templates/vue/frontend-runtime/async-data.ts +404 -0
- package/templates/vue/frontend-runtime/components.ts +386 -0
- package/templates/vue/frontend-runtime/cookies.ts +125 -0
- package/templates/vue/frontend-runtime/data-access.ts +132 -0
- package/templates/vue/frontend-runtime/dms-error.ts +11 -0
- package/templates/vue/frontend-runtime/errors.ts +145 -0
- package/templates/vue/frontend-runtime/fetch.ts +306 -0
- package/templates/vue/frontend-runtime/module-registration.ts +247 -0
- package/templates/vue/frontend-runtime/paged-data.ts +191 -0
- package/templates/vue/frontend-runtime/payload.ts +172 -0
- package/templates/vue/frontend-runtime/route.ts +328 -0
- package/templates/vue/frontend-runtime/sdk.ts +300 -0
- package/templates/vue/frontend-runtime/state.ts +167 -0
- package/templates/vue/frontend-runtime/types.ts +361 -0
- package/templates/vue/frontend-runtime/ui.ts +247 -0
- package/templates/vue/globals.d.ts +5 -0
- package/templates/vue/head-order.mjs +11 -13
- package/templates/vue/icon-bundle.ts +31 -0
- package/templates/vue/message-subset.ts +65 -0
- package/templates/vue/network-error.ts +4 -0
- package/templates/vue/prepare-types.mjs +0 -8
- package/templates/vue/server/auth/backend.mjs +4 -2
- package/templates/vue/server/auth/establish-endpoints.mjs +14 -9
- package/templates/vue/server/auth/session.mjs +4 -3
- package/templates/vue/server/auth-hooks.mjs +6 -6
- package/templates/vue/server/backend-proxies.mjs +6 -6
- package/templates/vue/server/backend-response.mjs +15 -14
- package/templates/vue/server/built-files.mjs +10 -8
- package/templates/vue/server/cli-report.mjs +7 -5
- package/templates/vue/server/client-error.mjs +11 -8
- package/templates/vue/server/client-manifest.mjs +9 -8
- package/templates/vue/server/content.mjs +2 -1
- package/templates/vue/server/dev-private-files.mjs +20 -15
- package/templates/vue/server/dev-styles.mjs +22 -25
- package/templates/vue/server/email-bundle-watch.mjs +0 -12
- package/templates/vue/server/email.mjs +20 -15
- package/templates/vue/server/favicon.mjs +12 -11
- package/templates/vue/server/homepage.mjs +8 -10
- package/templates/vue/server/icons.mjs +18 -9
- package/templates/vue/server/in-place-render.mjs +6 -8
- package/templates/vue/server/listen-host.mjs +2 -2
- package/templates/vue/server/module-event.mjs +5 -5
- package/templates/vue/server/module-routes.mjs +11 -7
- package/templates/vue/server/module-server.mjs +8 -7
- package/templates/vue/server/module-testing.mjs +7 -7
- package/templates/vue/server/page-props.mjs +14 -14
- package/templates/vue/server/path-patterns.mjs +4 -4
- package/templates/vue/server/proxy-compression.mjs +21 -13
- package/templates/vue/server/proxy-headers.mjs +5 -3
- package/templates/vue/server/proxy-redirects.mjs +7 -6
- package/templates/vue/server/proxy-transfers.mjs +15 -11
- package/templates/vue/server/public-endpoints.mjs +27 -19
- package/templates/vue/server/public-options.mjs +9 -7
- package/templates/vue/server/request-failure.mjs +4 -6
- package/templates/vue/server/request-routing.mjs +8 -11
- package/templates/vue/server/request-target.mjs +6 -6
- package/templates/vue/server/route-rules.mjs +7 -5
- package/templates/vue/server/static-files.mjs +19 -10
- package/templates/vue/server/web-body.mjs +3 -4
- package/templates/vue/server.mjs +32 -19
- package/templates/vue/sfc-global-types.mjs +27 -24
- package/templates/vue/ssr-renderer.ts +10 -1
- package/templates/vue/ui-inertia-page.mjs +7 -7
- package/templates/vue/ui-kbd.mjs +10 -10
- package/templates/vue/ui-lazy-icons.mjs +44 -0
- package/templates/vue/ui-request-state.mjs +9 -9
- package/templates/vue/vite.config.ts +2 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { router as inertiaRouter } from "@inertiajs/vue3";
|
|
2
|
+
import { defu } from "defu";
|
|
3
|
+
import {
|
|
4
|
+
type App,
|
|
5
|
+
type AppConfig,
|
|
6
|
+
type Component,
|
|
7
|
+
type ComponentInternalInstance,
|
|
8
|
+
camelize,
|
|
9
|
+
capitalize,
|
|
10
|
+
computed,
|
|
11
|
+
getCurrentInstance,
|
|
12
|
+
type InjectionKey,
|
|
13
|
+
inject,
|
|
14
|
+
} from "vue";
|
|
15
|
+
import type { Composer } from "vue-i18n";
|
|
16
|
+
import {
|
|
17
|
+
appConfigChanges,
|
|
18
|
+
assignInPlace,
|
|
19
|
+
mergeLayerAppConfigs,
|
|
20
|
+
withAppConfigChanges,
|
|
21
|
+
} from "../layer-app-config";
|
|
22
|
+
import {
|
|
23
|
+
type DmsFrontendEntry,
|
|
24
|
+
type DmsFrontendModuleRegistration,
|
|
25
|
+
type DmsFrontendRegistry,
|
|
26
|
+
type DmsFrontendRuntime,
|
|
27
|
+
type DmsFrontendSdk,
|
|
28
|
+
type DmsLayerAppConfig,
|
|
29
|
+
PLUGIN_ORDER,
|
|
30
|
+
} from "./module-registration";
|
|
31
|
+
import {
|
|
32
|
+
type DmsAppContext,
|
|
33
|
+
type DmsComponentPreloader,
|
|
34
|
+
type DmsI18n,
|
|
35
|
+
type DmsLocaleLoader,
|
|
36
|
+
type PublicRuntimeConfig,
|
|
37
|
+
} from "./types";
|
|
38
|
+
import {
|
|
39
|
+
normalizeDmsName,
|
|
40
|
+
normalizeDmsPageKey,
|
|
41
|
+
registerDmsComponentEntry,
|
|
42
|
+
} from "./components";
|
|
43
|
+
import {
|
|
44
|
+
appConfig,
|
|
45
|
+
createDmsFrontendRegistry,
|
|
46
|
+
forgetBrowserRuntime,
|
|
47
|
+
registry,
|
|
48
|
+
replaceRegistry,
|
|
49
|
+
runtimeConfig,
|
|
50
|
+
} from "./state";
|
|
51
|
+
import {
|
|
52
|
+
DmsClientOnly,
|
|
53
|
+
DmsLink,
|
|
54
|
+
failPage,
|
|
55
|
+
SETUP_FUNCTION_ERROR,
|
|
56
|
+
withinFailedSetup,
|
|
57
|
+
} from "./ui";
|
|
58
|
+
import { LOCALE_COOKIE, LOCALE_COOKIE_MAX_AGE, writeCookie } from "./cookies";
|
|
59
|
+
|
|
60
|
+
// What the last setup of the modules changed in the layers' app configs, which
|
|
61
|
+
// a development update of those configs keeps: see `updateDmsAppConfig`.
|
|
62
|
+
let appConfigSetupChanges: Record<string, unknown> = {};
|
|
63
|
+
|
|
64
|
+
export function useDmsInjection<T>(
|
|
65
|
+
key: string | InjectionKey<T>,
|
|
66
|
+
): T | undefined {
|
|
67
|
+
return inject(key as InjectionKey<T>, undefined);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function registerDmsFrontendEntry(
|
|
71
|
+
entries: Map<string, DmsFrontendEntry>,
|
|
72
|
+
name: string,
|
|
73
|
+
component: Component,
|
|
74
|
+
preload?: DmsComponentPreloader,
|
|
75
|
+
): void {
|
|
76
|
+
if (!entries.has(name)) entries.set(name, { component, preload });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** `name` behind `prefix`, as a PascalCase component name. */
|
|
80
|
+
function prefixedComponentName(prefix: string, name: string): string {
|
|
81
|
+
return prefix ? `${prefix}${capitalize(camelize(name))}` : name;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function createSdk(
|
|
85
|
+
target: DmsFrontendRegistry,
|
|
86
|
+
{
|
|
87
|
+
name: module,
|
|
88
|
+
module: { componentPrefix = "" },
|
|
89
|
+
options,
|
|
90
|
+
}: DmsFrontendModuleRegistration,
|
|
91
|
+
): DmsFrontendSdk {
|
|
92
|
+
return {
|
|
93
|
+
options,
|
|
94
|
+
registerComponent: (name, component, { prefix = true } = {}) =>
|
|
95
|
+
registerDmsComponentEntry(target.components, {
|
|
96
|
+
name: prefix ? prefixedComponentName(componentPrefix, name) : name,
|
|
97
|
+
component,
|
|
98
|
+
module,
|
|
99
|
+
unprefixed: !prefix,
|
|
100
|
+
}),
|
|
101
|
+
registerPage: (name, component, preload) => {
|
|
102
|
+
const key = normalizeDmsPageKey(name);
|
|
103
|
+
registerDmsFrontendEntry(target.pages, key, component, preload);
|
|
104
|
+
},
|
|
105
|
+
registerDynamicPage: (name, component, preload) =>
|
|
106
|
+
registerDmsFrontendEntry(target.dynamicPages, name, component, preload),
|
|
107
|
+
registerLayout: (name, component, preload) => {
|
|
108
|
+
const key = normalizeDmsName(name);
|
|
109
|
+
registerDmsFrontendEntry(target.layouts, key, component, preload);
|
|
110
|
+
},
|
|
111
|
+
registerErrorPage: (component, preload) => {
|
|
112
|
+
target.errorPage ??= { component, preload };
|
|
113
|
+
},
|
|
114
|
+
registerPlugin: (setup, options = {}) =>
|
|
115
|
+
target.pluginSetups.push({
|
|
116
|
+
setup,
|
|
117
|
+
clientOnly: options.clientOnly ?? false,
|
|
118
|
+
enforce: options.enforce ?? "default",
|
|
119
|
+
}),
|
|
120
|
+
registerMiddleware: (name, handler, options = {}) => {
|
|
121
|
+
if (!target.namedMiddleware.has(name))
|
|
122
|
+
target.namedMiddleware.set(name, handler);
|
|
123
|
+
if (options.global) target.middleware.push(handler);
|
|
124
|
+
if (options.parallel) target.parallelMiddleware.add(handler);
|
|
125
|
+
},
|
|
126
|
+
registerAccessRedirect: (code, path) => {
|
|
127
|
+
if (!target.accessRedirects.has(code))
|
|
128
|
+
target.accessRedirects.set(code, path);
|
|
129
|
+
},
|
|
130
|
+
provide: (key, value) => {
|
|
131
|
+
if (!target.injections.has(key)) target.injections.set(key, value);
|
|
132
|
+
},
|
|
133
|
+
use: (plugin) => target.plugins.push(plugin),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Sets the frontend modules up in their generated priority order, into a
|
|
139
|
+
* registry of their own that replaces the previous one once complete.
|
|
140
|
+
*
|
|
141
|
+
* The browser and a production server set the modules up once. A development
|
|
142
|
+
* server sets them up again each time Vite evaluates the server renderer anew
|
|
143
|
+
* after a layer changes, but keeps this module and its state: registering into
|
|
144
|
+
* the previous registry, the new components lost their names to the ones they
|
|
145
|
+
* replace, so the server kept rendering the old ones, and every list gained the
|
|
146
|
+
* same entries once more. A render still in progress keeps reading a complete
|
|
147
|
+
* registry: the new one is only published once every module is set up.
|
|
148
|
+
*
|
|
149
|
+
* `useDmsAppConfig()` holds the layers' app configs merged, highest precedence
|
|
150
|
+
* first, before the first module is set up: a project's layer overrides the
|
|
151
|
+
* DMS's, which fills in the rest. A setup may still read and extend it.
|
|
152
|
+
*/
|
|
153
|
+
export async function setupFrontendModules(
|
|
154
|
+
registrations: DmsFrontendModuleRegistration[],
|
|
155
|
+
layerAppConfigs: DmsLayerAppConfig[] = [],
|
|
156
|
+
): Promise<void> {
|
|
157
|
+
const next = createDmsFrontendRegistry();
|
|
158
|
+
const configs = layerAppConfigs.map(({ config }) => config);
|
|
159
|
+
// Modules read and extend these two while they are set up, so they restart
|
|
160
|
+
// here rather than being published with the registry: the public options
|
|
161
|
+
// empty, the app config from the layers alone.
|
|
162
|
+
runtimeConfig.value.public = {} as PublicRuntimeConfig;
|
|
163
|
+
appConfig.value = mergeLayerAppConfigs(configs);
|
|
164
|
+
for (const registration of registrations) {
|
|
165
|
+
runtimeConfig.value.public = defu(
|
|
166
|
+
runtimeConfig.value.public,
|
|
167
|
+
registration.options.public,
|
|
168
|
+
);
|
|
169
|
+
await registration.module.setup(createSdk(next, registration));
|
|
170
|
+
}
|
|
171
|
+
appConfigSetupChanges = appConfigChanges(
|
|
172
|
+
mergeLayerAppConfigs(configs),
|
|
173
|
+
appConfig.value,
|
|
174
|
+
);
|
|
175
|
+
replaceRegistry(next);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Development only: applies the layers' app configs, edited while the page
|
|
180
|
+
* is open, to `useDmsAppConfig()`, with what the setup of the modules changed
|
|
181
|
+
* in the previous ones still on top. The object is updated in place, records
|
|
182
|
+
* included, since components hold it: the modules are not set up again.
|
|
183
|
+
*/
|
|
184
|
+
export function updateDmsAppConfig(layerAppConfigs: DmsLayerAppConfig[]): void {
|
|
185
|
+
const layered = mergeLayerAppConfigs(
|
|
186
|
+
layerAppConfigs.map(({ config }) => config),
|
|
187
|
+
);
|
|
188
|
+
assignInPlace(
|
|
189
|
+
appConfig.value,
|
|
190
|
+
withAppConfigChanges(layered, appConfigSetupChanges),
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function installDmsPlugins(
|
|
195
|
+
app: App,
|
|
196
|
+
i18n: Composer,
|
|
197
|
+
runtime: DmsFrontendRuntime,
|
|
198
|
+
loadLocaleMessages: DmsLocaleLoader,
|
|
199
|
+
supportedLocales: string[],
|
|
200
|
+
): Promise<() => Promise<void>> {
|
|
201
|
+
const dmsI18n = i18n as DmsI18n;
|
|
202
|
+
const hooks = new Map<string, Array<() => void | Promise<void>>>();
|
|
203
|
+
const appContext: DmsAppContext = {
|
|
204
|
+
vueApp: app,
|
|
205
|
+
$i18n: dmsI18n,
|
|
206
|
+
provide: (key, value) => {
|
|
207
|
+
app.provide(key, value);
|
|
208
|
+
app.config.globalProperties[`$${key}`] = value;
|
|
209
|
+
},
|
|
210
|
+
runWithContext: (callback) => app.runWithContext(callback),
|
|
211
|
+
hook: (name, callback) =>
|
|
212
|
+
hooks.set(name, [...(hooks.get(name) ?? []), callback]),
|
|
213
|
+
};
|
|
214
|
+
runtime.appContext = appContext;
|
|
215
|
+
if (typeof window !== "undefined" && !runtime.hasNavigationListener) {
|
|
216
|
+
runtime.hasNavigationListener = true;
|
|
217
|
+
const stopNavigationListener = inertiaRouter.on("before", () => {
|
|
218
|
+
runtime.currentError.value = null;
|
|
219
|
+
});
|
|
220
|
+
app.onUnmount(() => {
|
|
221
|
+
stopNavigationListener();
|
|
222
|
+
runtime.hasNavigationListener = false;
|
|
223
|
+
forgetBrowserRuntime(runtime);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
const failedSetups = new WeakSet<ComponentInternalInstance>();
|
|
227
|
+
const warnOutsideFailedSetups: AppConfig["warnHandler"] = (
|
|
228
|
+
message,
|
|
229
|
+
instance,
|
|
230
|
+
trace,
|
|
231
|
+
) => {
|
|
232
|
+
// The comment Vue renders in place of an `undefined` component has no
|
|
233
|
+
// public instance: the template rendering it is the one that failed.
|
|
234
|
+
const source = instance?.$ ?? getCurrentInstance();
|
|
235
|
+
if (!withinFailedSetup(source, failedSetups))
|
|
236
|
+
console.warn(`[Vue warn]: ${message}${trace ? `\n${trace}` : ""}`);
|
|
237
|
+
};
|
|
238
|
+
app.config.errorHandler = (reason, instance, info) => {
|
|
239
|
+
if (instance && info === SETUP_FUNCTION_ERROR) {
|
|
240
|
+
failedSetups.add(instance.$);
|
|
241
|
+
// From the first failure on only: until then Vue keeps its own output,
|
|
242
|
+
// whose component trace shows the props. An app that already has a
|
|
243
|
+
// handler keeps it. A server renders a single page per app; in the
|
|
244
|
+
// browser the app outlives Inertia visits, so once a page has failed
|
|
245
|
+
// the handler stays for the session and later warnings print as plain
|
|
246
|
+
// text (no inspectable objects), still all of them.
|
|
247
|
+
app.config.warnHandler ??= warnOutsideFailedSetups;
|
|
248
|
+
}
|
|
249
|
+
failPage(runtime, reason);
|
|
250
|
+
};
|
|
251
|
+
app.config.globalProperties.$dms = appContext;
|
|
252
|
+
Reflect.set(app, "$dms", appContext);
|
|
253
|
+
Reflect.set(app, "$i18n", i18n);
|
|
254
|
+
Reflect.set(appContext.vueApp, "$dms", appContext);
|
|
255
|
+
Reflect.set(app.config.globalProperties, "$i18n", i18n);
|
|
256
|
+
dmsI18n.setLocale = async (locale: string) => {
|
|
257
|
+
const messages = await loadLocaleMessages(locale);
|
|
258
|
+
i18n.setLocaleMessage(locale, messages);
|
|
259
|
+
i18n.locale.value = locale;
|
|
260
|
+
if (typeof document !== "undefined" && supportedLocales.includes(locale))
|
|
261
|
+
writeCookie(LOCALE_COOKIE, locale, {
|
|
262
|
+
maxAge: LOCALE_COOKIE_MAX_AGE,
|
|
263
|
+
sameSite: "lax",
|
|
264
|
+
secure: globalThis.location?.protocol === "https:",
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
dmsI18n.locales = computed(() =>
|
|
268
|
+
supportedLocales.map((code) => ({
|
|
269
|
+
code,
|
|
270
|
+
name: new Intl.DisplayNames([code], { type: "language" }).of(code),
|
|
271
|
+
})),
|
|
272
|
+
);
|
|
273
|
+
app.component("DmsLink", DmsLink);
|
|
274
|
+
app.component("DmsClientOnly", DmsClientOnly);
|
|
275
|
+
registry.components.forEach(({ component, name }) => {
|
|
276
|
+
app.component(name, component);
|
|
277
|
+
});
|
|
278
|
+
registry.injections.forEach((value, key) => {
|
|
279
|
+
app.provide(key, value);
|
|
280
|
+
});
|
|
281
|
+
registry.plugins.forEach((plugin) => {
|
|
282
|
+
app.use(plugin);
|
|
283
|
+
});
|
|
284
|
+
// Stable: within a group, the modules' priority order stays.
|
|
285
|
+
const pluginSetups = [...registry.pluginSetups].sort(
|
|
286
|
+
(left, right) => PLUGIN_ORDER[left.enforce] - PLUGIN_ORDER[right.enforce],
|
|
287
|
+
);
|
|
288
|
+
for (const registration of pluginSetups) {
|
|
289
|
+
if (registration.clientOnly && typeof window === "undefined") continue;
|
|
290
|
+
await runtime.scope.run(() =>
|
|
291
|
+
app.runWithContext(() => registration.setup(appContext)),
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
// What plugins start without awaiting it is still part of the page a
|
|
295
|
+
// server renders: its HTML shows it, so the render waits for it.
|
|
296
|
+
if (runtime.isServer) await Promise.all(runtime.serverPrefetches);
|
|
297
|
+
return async () => {
|
|
298
|
+
for (const callback of hooks.get("app:mounted") ?? []) await callback();
|
|
299
|
+
};
|
|
300
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { ofetch } from "ofetch";
|
|
2
|
+
import {
|
|
3
|
+
type App,
|
|
4
|
+
effectScope,
|
|
5
|
+
getCurrentInstance,
|
|
6
|
+
hasInjectionContext,
|
|
7
|
+
type InjectionKey,
|
|
8
|
+
inject,
|
|
9
|
+
onScopeDispose,
|
|
10
|
+
reactive,
|
|
11
|
+
ref,
|
|
12
|
+
shallowReactive,
|
|
13
|
+
} from "vue";
|
|
14
|
+
import {
|
|
15
|
+
type DmsFrontendRegistry,
|
|
16
|
+
type DmsFrontendRuntime,
|
|
17
|
+
type DmsRuntimeHooks,
|
|
18
|
+
} from "./module-registration";
|
|
19
|
+
import {
|
|
20
|
+
type DmsAppContext,
|
|
21
|
+
type DmsRoute,
|
|
22
|
+
type DmsRuntimeConfig,
|
|
23
|
+
} from "./types";
|
|
24
|
+
|
|
25
|
+
/** Creates the empty registry one setup of the frontend modules fills. */
|
|
26
|
+
export function createDmsFrontendRegistry(): DmsFrontendRegistry {
|
|
27
|
+
return {
|
|
28
|
+
components: new Map(),
|
|
29
|
+
pages: new Map(),
|
|
30
|
+
dynamicPages: new Map(),
|
|
31
|
+
layouts: new Map(),
|
|
32
|
+
plugins: [],
|
|
33
|
+
pluginSetups: [],
|
|
34
|
+
middleware: [],
|
|
35
|
+
namedMiddleware: new Map(),
|
|
36
|
+
parallelMiddleware: new WeakSet(),
|
|
37
|
+
accessRedirects: new Map(),
|
|
38
|
+
injections: new Map(),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Replaced, never refilled, by each setup of the modules: see
|
|
43
|
+
// `setupFrontendModules`.
|
|
44
|
+
export let registry = createDmsFrontendRegistry();
|
|
45
|
+
export const runtimeConfig = ref<DmsRuntimeConfig>({
|
|
46
|
+
public: {},
|
|
47
|
+
} as DmsRuntimeConfig);
|
|
48
|
+
export const appConfig = ref<Record<string, unknown>>({});
|
|
49
|
+
const DMS_RUNTIME_KEY: InjectionKey<DmsFrontendRuntime> = Symbol("dms-runtime");
|
|
50
|
+
type DmsRuntimeContext = DmsAppContext["runWithContext"];
|
|
51
|
+
export const runtimeContexts = new WeakMap<
|
|
52
|
+
DmsFrontendRuntime,
|
|
53
|
+
DmsRuntimeContext
|
|
54
|
+
>();
|
|
55
|
+
let serverRuntimeResolver: (() => DmsFrontendRuntime | undefined) | undefined;
|
|
56
|
+
let browserRuntime: DmsFrontendRuntime | undefined;
|
|
57
|
+
|
|
58
|
+
export function replaceRegistry(next: DmsFrontendRegistry): void {
|
|
59
|
+
registry = next;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function forgetBrowserRuntime(runtime: DmsFrontendRuntime): void {
|
|
63
|
+
if (browserRuntime === runtime) browserRuntime = undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Creates mutable runtime state owned by one Vue application. */
|
|
67
|
+
export function createDmsFrontendRuntime(
|
|
68
|
+
serverFetch?: typeof ofetch,
|
|
69
|
+
hydratedAsyncData?: Record<string, unknown>,
|
|
70
|
+
isServer = false,
|
|
71
|
+
requestCookies?: string,
|
|
72
|
+
): DmsFrontendRuntime {
|
|
73
|
+
const runtime: DmsFrontendRuntime = {
|
|
74
|
+
requestCookies,
|
|
75
|
+
sharedState: new Map(),
|
|
76
|
+
asyncData: shallowReactive(new Map()),
|
|
77
|
+
hydratedAsyncData: new Map(Object.entries(hydratedAsyncData ?? {})),
|
|
78
|
+
hydratedAsyncErrors: new Map(),
|
|
79
|
+
transferredState: new Set(),
|
|
80
|
+
// A browser handed the server's data, even none, hydrates its HTML.
|
|
81
|
+
hydrating: !isServer && hydratedAsyncData !== undefined,
|
|
82
|
+
serverPrefetches: new Set(),
|
|
83
|
+
asyncDataRuns: new Set(),
|
|
84
|
+
boundAsyncData: new Set(),
|
|
85
|
+
asyncDataClock: 0,
|
|
86
|
+
runtimeHooks: new Map(),
|
|
87
|
+
serverFetch,
|
|
88
|
+
isServer,
|
|
89
|
+
currentError: ref<Error | null>(null),
|
|
90
|
+
route: reactive<DmsRoute>({
|
|
91
|
+
fullPath: "/",
|
|
92
|
+
path: "/",
|
|
93
|
+
query: {},
|
|
94
|
+
params: {},
|
|
95
|
+
meta: {},
|
|
96
|
+
matched: [],
|
|
97
|
+
}),
|
|
98
|
+
hasNavigationListener: false,
|
|
99
|
+
pageLoading: ref(false),
|
|
100
|
+
scope: effectScope(true),
|
|
101
|
+
};
|
|
102
|
+
// A server render stops its scope once it has answered: whatever it still
|
|
103
|
+
// fetches then would reach nobody.
|
|
104
|
+
runtime.scope.run(() =>
|
|
105
|
+
onScopeDispose(() => {
|
|
106
|
+
for (const run of runtime.asyncDataRuns) run.abort();
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
109
|
+
return runtime;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Makes a runtime available before application plugins are installed. */
|
|
113
|
+
export function provideDmsFrontendRuntime(
|
|
114
|
+
app: App,
|
|
115
|
+
runtime: DmsFrontendRuntime,
|
|
116
|
+
): void {
|
|
117
|
+
app.provide(DMS_RUNTIME_KEY, runtime);
|
|
118
|
+
runtimeContexts.set(runtime, (callback) => app.runWithContext(callback));
|
|
119
|
+
if (typeof window !== "undefined") browserRuntime = runtime;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Registers the request-local runtime resolver used by the SSR entry point. */
|
|
123
|
+
export function setDmsServerRuntimeResolver(
|
|
124
|
+
resolver: () => DmsFrontendRuntime | undefined,
|
|
125
|
+
): void {
|
|
126
|
+
serverRuntimeResolver = resolver;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function useDmsRuntime(): DmsFrontendRuntime {
|
|
130
|
+
const instance = getCurrentInstance();
|
|
131
|
+
const provided = instance?.appContext.provides[DMS_RUNTIME_KEY as symbol] as
|
|
132
|
+
| DmsFrontendRuntime
|
|
133
|
+
| undefined;
|
|
134
|
+
if (provided) return provided;
|
|
135
|
+
const contextual = hasInjectionContext()
|
|
136
|
+
? inject(DMS_RUNTIME_KEY, undefined)
|
|
137
|
+
: undefined;
|
|
138
|
+
if (contextual) return contextual;
|
|
139
|
+
if (typeof window !== "undefined" && browserRuntime) return browserRuntime;
|
|
140
|
+
const serverRuntime = serverRuntimeResolver?.();
|
|
141
|
+
if (serverRuntime) return serverRuntime;
|
|
142
|
+
throw new Error("DMS runtime is unavailable outside a Vue application");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Returns lifecycle hooks isolated to the current application runtime. */
|
|
146
|
+
export function useDmsRuntimeHooks(): DmsRuntimeHooks {
|
|
147
|
+
const runtime = useDmsRuntime();
|
|
148
|
+
return {
|
|
149
|
+
hook(name, callback) {
|
|
150
|
+
runtime.runtimeHooks.set(name, [
|
|
151
|
+
...(runtime.runtimeHooks.get(name) ?? []),
|
|
152
|
+
callback,
|
|
153
|
+
]);
|
|
154
|
+
return () =>
|
|
155
|
+
runtime.runtimeHooks.set(
|
|
156
|
+
name,
|
|
157
|
+
(runtime.runtimeHooks.get(name) ?? []).filter(
|
|
158
|
+
(entry) => entry !== callback,
|
|
159
|
+
),
|
|
160
|
+
);
|
|
161
|
+
},
|
|
162
|
+
async callHook(name, ...args) {
|
|
163
|
+
for (const callback of runtime.runtimeHooks.get(name) ?? [])
|
|
164
|
+
await callback(...args);
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|