@forgebook/nuxt 0.0.0-stage → 0.2.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -2
  3. package/dist/src/app/components.d.ts +119 -0
  4. package/dist/src/app/components.js +196 -0
  5. package/dist/src/app/data.d.ts +109 -0
  6. package/dist/src/app/data.js +155 -0
  7. package/dist/src/app/define.d.ts +30 -0
  8. package/dist/src/app/define.js +30 -0
  9. package/dist/src/app/errors.d.ts +43 -0
  10. package/dist/src/app/errors.js +41 -0
  11. package/dist/src/app/fetch.d.ts +63 -0
  12. package/dist/src/app/fetch.js +73 -0
  13. package/dist/src/app/head.d.ts +28 -0
  14. package/dist/src/app/head.js +23 -0
  15. package/dist/src/app/router.d.ts +69 -0
  16. package/dist/src/app/router.js +93 -0
  17. package/dist/src/app/state.d.ts +107 -0
  18. package/dist/src/app/state.js +137 -0
  19. package/dist/src/asyncDataRun.d.ts +39 -0
  20. package/dist/src/asyncDataRun.js +128 -0
  21. package/dist/src/nuxtError.d.ts +50 -0
  22. package/dist/src/nuxtError.js +65 -0
  23. package/dist/src/nuxtRenderer.d.ts +14 -0
  24. package/dist/src/nuxtRenderer.js +67 -0
  25. package/dist/src/nuxtStore.d.ts +144 -0
  26. package/dist/src/nuxtStore.js +196 -0
  27. package/dist/src/pageProject.d.ts +11 -0
  28. package/dist/src/pageProject.js +11 -0
  29. package/dist/src/parameters.d.ts +67 -0
  30. package/dist/src/parameters.js +89 -0
  31. package/dist/src/project.d.ts +49 -0
  32. package/dist/src/project.js +7 -0
  33. package/dist/src/renderer.d.ts +9 -0
  34. package/dist/src/renderer.js +10 -0
  35. package/dist/src/stories.d.ts +54 -0
  36. package/dist/src/stories.js +11 -0
  37. package/dist/src/storyCode.d.ts +3 -0
  38. package/dist/src/storyCode.js +9 -0
  39. package/dist/src/storyFetch.d.ts +74 -0
  40. package/dist/src/storyFetch.js +146 -0
  41. package/dist/src/storyNuxt.d.ts +48 -0
  42. package/dist/src/storyNuxt.js +26 -0
  43. package/dist/src/vite/componentNames.d.ts +36 -0
  44. package/dist/src/vite/componentNames.js +72 -0
  45. package/dist/src/vite/extractDocs.d.ts +16 -0
  46. package/dist/src/vite/extractDocs.js +22 -0
  47. package/dist/src/vite/nuxt.d.ts +13 -0
  48. package/dist/src/vite/nuxt.js +36 -0
  49. package/dist/src/vite/nuxtConfig.d.ts +85 -0
  50. package/dist/src/vite/nuxtConfig.js +261 -0
  51. package/dist/src/vite/nuxtPlugin.d.ts +51 -0
  52. package/dist/src/vite/nuxtPlugin.js +466 -0
  53. package/dist/src/vite/pageRoutes.d.ts +23 -0
  54. package/dist/src/vite/pageRoutes.js +88 -0
  55. package/dist/src/vite/placeKeys.d.ts +10 -0
  56. package/dist/src/vite/placeKeys.js +117 -0
  57. package/dist/src/vite/standIns.d.ts +26 -0
  58. package/dist/src/vite/standIns.js +96 -0
  59. package/package.json +118 -3
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Nuxt's state and app composables in a catalogue: `useState`, `useCookie`, `useRuntimeConfig`, `useAppConfig`,
3
+ * `useNuxtApp`, `callOnce`, `onNuxtReady` and the request's composables.
4
+ *
5
+ * @remarks
6
+ * A story gives what they start with (`parameters.nuxt.state`, `cookies`, `runtimeConfig`, `appConfig`), over what
7
+ * the project gives. Every value lives in the story's store, so two components of one story share a key, as two
8
+ * components of a Nuxt page do, and a story drawn anew starts over. A cookie is never written to the frame's document:
9
+ * the story's store keeps it, so a story cannot leave one behind for the next.
10
+ *
11
+ * The page has no request: `useRequestHeaders` returns none and `useRequestEvent` nothing, as in Nuxt's browser code.
12
+ *
13
+ * Internal: the code the plugin writes imports it (`#imports`, `#app`, `#components`); a story or a component imports
14
+ * Nuxt's names as in the app, never this module, whose exports may change in any release.
15
+ *
16
+ * @module
17
+ */
18
+ import { type Ref } from "vue";
19
+ import { type NuxtAppStandIn } from "../nuxtStore.ts";
20
+ import type { PublicRuntimeConfig } from "../project.ts";
21
+ import type { StoryFetch } from "../storyFetch.ts";
22
+ /**
23
+ * A value shared by every component of the story under a key, starting with the story's value for the key, else
24
+ * `init`'s.
25
+ *
26
+ * @param key - the key.
27
+ * @param init - the value it starts with.
28
+ */
29
+ export declare function useState<TValue>(key: string, init?: () => TValue): Ref<TValue>;
30
+ /**
31
+ * A value of its own, under a key of its own.
32
+ *
33
+ * @param init - the value it starts with.
34
+ */
35
+ export declare function useState<TValue>(init?: () => TValue): Ref<TValue>;
36
+ /**
37
+ * Empties the state of keys.
38
+ *
39
+ * @param keys - a key, keys, or none for all.
40
+ */
41
+ export declare const clearNuxtState: (keys?: string | readonly string[]) => void;
42
+ /** What `useCookie` takes besides a name. */
43
+ export type CookieOptions<TValue> = {
44
+ /** The value when the story gives none. */
45
+ readonly default?: () => TValue;
46
+ /** Whether the ref can be written: no effect, it always can. */
47
+ readonly readonly?: boolean;
48
+ /** Nuxt's other cookie options (`maxAge`, `path`, `sameSite`…): no effect, the cookie stays in the story. */
49
+ readonly [option: string]: unknown;
50
+ };
51
+ /**
52
+ * A cookie: the story's value for the name, else the default; written in the story only.
53
+ *
54
+ * @param name - the cookie's name.
55
+ * @param options - its default.
56
+ */
57
+ export declare function useCookie<TValue = string | null | undefined>(name: string, options?: CookieOptions<TValue>): Ref<TValue>;
58
+ /** Reads a cookie again from the document: it lives in the story, so there is nothing to read. */
59
+ export declare const refreshCookie: () => void;
60
+ /** The runtime config: the project's public values and app paths, the story's public values over them. */
61
+ export declare const useRuntimeConfig: () => PublicRuntimeConfig;
62
+ /** The app config: the project's `app.config` and `appConfig`, the story's over them. */
63
+ export declare const useAppConfig: () => Record<string, unknown>;
64
+ /**
65
+ * Changes the app config, as Nuxt's `updateAppConfig`.
66
+ *
67
+ * @param values - the values, over the config's.
68
+ */
69
+ export declare const updateAppConfig: (values: Readonly<Record<string, unknown>>) => void;
70
+ /** The story's app: its payload, its config, its router, what plugins would have provided. */
71
+ export declare const useNuxtApp: () => NuxtAppStandIn;
72
+ /** The story's app, which there always is in a story. */
73
+ export declare const tryUseNuxtApp: () => NuxtAppStandIn;
74
+ /**
75
+ * Runs a function once for the story, as Nuxt runs one once for a request.
76
+ *
77
+ * @param first - the key it runs once under, or the function, keyed by its place in the code.
78
+ * @param second - the function, or the key the plugin wrote for the call's place.
79
+ * @param third - the key the plugin wrote for the call's place, after the options.
80
+ */
81
+ export declare const callOnce: (first: string | (() => unknown), second?: (() => unknown) | Readonly<Record<string, unknown>> | string, third?: string) => Promise<void>;
82
+ /**
83
+ * Runs a function once the story is drawn, as Nuxt runs it once the app is.
84
+ *
85
+ * @param run - the function.
86
+ */
87
+ export declare const onNuxtReady: (run: () => unknown) => void;
88
+ /** The request's headers: there is no request in the page, so none. */
89
+ export declare const useRequestHeaders: () => Record<string, string>;
90
+ /** One of the request's headers: there is no request in the page, so none. */
91
+ export declare const useRequestHeader: () => undefined;
92
+ /** The request: none in the page. */
93
+ export declare const useRequestEvent: () => undefined;
94
+ /** The `$fetch` that passes the request's headers on: `$fetch` itself, as in Nuxt's browser code. */
95
+ export declare const useRequestFetch: () => StoryFetch;
96
+ /** The address the story's route has, on the frame's origin. */
97
+ export declare const useRequestURL: () => URL;
98
+ /** Whether the page was prerendered: never, in a story. */
99
+ export declare const isPrerendered: () => boolean;
100
+ /** The loading indicator's state: nothing loads between pages in a story. */
101
+ export declare const useLoadingIndicator: () => {
102
+ readonly progress: Ref<number>;
103
+ readonly isLoading: Ref<boolean>;
104
+ readonly start: () => void;
105
+ readonly finish: () => void;
106
+ readonly clear: () => void;
107
+ };
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Nuxt's state and app composables in a catalogue: `useState`, `useCookie`, `useRuntimeConfig`, `useAppConfig`,
3
+ * `useNuxtApp`, `callOnce`, `onNuxtReady` and the request's composables.
4
+ *
5
+ * @remarks
6
+ * A story gives what they start with (`parameters.nuxt.state`, `cookies`, `runtimeConfig`, `appConfig`), over what
7
+ * the project gives. Every value lives in the story's store, so two components of one story share a key, as two
8
+ * components of a Nuxt page do, and a story drawn anew starts over. A cookie is never written to the frame's document:
9
+ * the story's store keeps it, so a story cannot leave one behind for the next.
10
+ *
11
+ * The page has no request: `useRequestHeaders` returns none and `useRequestEvent` nothing, as in Nuxt's browser code.
12
+ *
13
+ * Internal: the code the plugin writes imports it (`#imports`, `#app`, `#components`); a story or a component imports
14
+ * Nuxt's names as in the app, never this module, whose exports may change in any release.
15
+ *
16
+ * @module
17
+ */
18
+ import { onMounted, ref } from "vue";
19
+ import { useNuxtStore } from "../nuxtStore.js";
20
+ /**
21
+ * The first of two arguments that is a function: the one a call gives where the key may come first or not.
22
+ *
23
+ * @param first - the first argument.
24
+ * @param second - the second.
25
+ */
26
+ const firstFunction = (first, second) => {
27
+ if (typeof first === "function")
28
+ return () => Reflect.apply(first, undefined, []);
29
+ if (typeof second === "function")
30
+ return () => Reflect.apply(second, undefined, []);
31
+ return undefined;
32
+ };
33
+ export function useState(first, second) {
34
+ const store = useNuxtStore();
35
+ // a text after the init is the key the plugin wrote for the call's place
36
+ const placeKey = typeof second === "string" ? second : undefined;
37
+ const key = typeof first === "string" ? first : (placeKey ?? store.nextKey("state"));
38
+ const init = firstFunction(first, second);
39
+ const known = store.state.get(key);
40
+ if (known !== undefined)
41
+ return known;
42
+ const value = ref(Object.hasOwn(store.storyState, key) ? store.storyState[key] : init?.());
43
+ store.state.set(key, value);
44
+ return value;
45
+ }
46
+ /**
47
+ * Empties the state of keys.
48
+ *
49
+ * @param keys - a key, keys, or none for all.
50
+ */
51
+ export const clearNuxtState = (keys) => {
52
+ const { state } = useNuxtStore();
53
+ const given = typeof keys === "string" ? [keys] : keys;
54
+ for (const key of given ?? [...state.keys()]) {
55
+ const value = state.get(key);
56
+ if (value !== undefined)
57
+ value.value = undefined;
58
+ }
59
+ };
60
+ export function useCookie(name, options = {}) {
61
+ const { cookies, storyCookies } = useNuxtStore();
62
+ const known = cookies.get(name);
63
+ if (known !== undefined)
64
+ return known;
65
+ const value = ref(Object.hasOwn(storyCookies, name) ? storyCookies[name] : options.default?.());
66
+ cookies.set(name, value);
67
+ return value;
68
+ }
69
+ /** Reads a cookie again from the document: it lives in the story, so there is nothing to read. */
70
+ export const refreshCookie = () => undefined;
71
+ /** The runtime config: the project's public values and app paths, the story's public values over them. */
72
+ export const useRuntimeConfig = () => useNuxtStore().runtimeConfig;
73
+ /** The app config: the project's `app.config` and `appConfig`, the story's over them. */
74
+ export const useAppConfig = () => useNuxtStore().appConfig;
75
+ /**
76
+ * Changes the app config, as Nuxt's `updateAppConfig`.
77
+ *
78
+ * @param values - the values, over the config's.
79
+ */
80
+ export const updateAppConfig = (values) => {
81
+ Object.assign(useNuxtStore().appConfig, values);
82
+ };
83
+ /** The story's app: its payload, its config, its router, what plugins would have provided. */
84
+ export const useNuxtApp = () => useNuxtStore().nuxtApp;
85
+ /** The story's app, which there always is in a story. */
86
+ export const tryUseNuxtApp = () => useNuxtStore().nuxtApp;
87
+ /**
88
+ * Runs a function once for the story, as Nuxt runs one once for a request.
89
+ *
90
+ * @param first - the key it runs once under, or the function, keyed by its place in the code.
91
+ * @param second - the function, or the key the plugin wrote for the call's place.
92
+ * @param third - the key the plugin wrote for the call's place, after the options.
93
+ */
94
+ export const callOnce = async (first, second, third) => {
95
+ const { once, nextKey } = useNuxtStore();
96
+ const placeKey = typeof second === "string" ? second : third;
97
+ const key = typeof first === "string" ? first : (placeKey ?? nextKey("once"));
98
+ const work = firstFunction(first, second);
99
+ if (once.has(key) || work === undefined)
100
+ return;
101
+ once.add(key);
102
+ await work();
103
+ };
104
+ /**
105
+ * Runs a function once the story is drawn, as Nuxt runs it once the app is.
106
+ *
107
+ * @param run - the function.
108
+ */
109
+ export const onNuxtReady = (run) => {
110
+ onMounted(() => {
111
+ void run();
112
+ });
113
+ };
114
+ /** The request's headers: there is no request in the page, so none. */
115
+ export const useRequestHeaders = () => ({});
116
+ /** One of the request's headers: there is no request in the page, so none. */
117
+ export const useRequestHeader = () => undefined;
118
+ /** The request: none in the page. */
119
+ export const useRequestEvent = () => undefined;
120
+ /** The `$fetch` that passes the request's headers on: `$fetch` itself, as in Nuxt's browser code. */
121
+ export const useRequestFetch = () => useNuxtStore().fetch;
122
+ /** The address the story's route has, on the frame's origin. */
123
+ export const useRequestURL = () => {
124
+ const { router, runtimeConfig } = useNuxtStore();
125
+ const base = runtimeConfig.app.baseURL.replace(/\/$/, "");
126
+ return new URL(`${base}${router.currentRoute.value.fullPath}`, globalThis.location?.origin ?? "http://localhost");
127
+ };
128
+ /** Whether the page was prerendered: never, in a story. */
129
+ export const isPrerendered = () => false;
130
+ /** The loading indicator's state: nothing loads between pages in a story. */
131
+ export const useLoadingIndicator = () => ({
132
+ progress: ref(0),
133
+ isLoading: ref(false),
134
+ start: () => undefined,
135
+ finish: () => undefined,
136
+ clear: () => undefined
137
+ });
@@ -0,0 +1,39 @@
1
+ import type { AsyncDataEntry, NuxtAppStandIn, NuxtStore } from "./nuxtStore.ts";
2
+ /** The handler of `useAsyncData`: what fetches the data. */
3
+ export type AsyncDataHandler<TResult> = (nuxtApp: NuxtAppStandIn, context: {
4
+ readonly signal: AbortSignal;
5
+ }) => Promise<TResult> | TResult;
6
+ /** What a run does with what the handler returned. */
7
+ export type RunOptions = {
8
+ /** The data before the handler returns, and after a failure. */
9
+ readonly default?: () => unknown;
10
+ /** Changes what the handler returned. */
11
+ readonly transform?: (input: unknown) => unknown;
12
+ /** The keys of the result kept; all by default. */
13
+ readonly pick?: readonly string[];
14
+ };
15
+ /**
16
+ * The entry of a key: the one a call made before, or a new one.
17
+ *
18
+ * @param store - the story's store.
19
+ * @param key - the key.
20
+ * @param defaultData - the data a new entry starts with.
21
+ */
22
+ export declare const entryOf: (store: NuxtStore, key: string, defaultData: () => unknown) => AsyncDataEntry;
23
+ /**
24
+ * Starts a run of a key's handler, and keeps it as the run under way until it ends.
25
+ *
26
+ * @param store - the story's store.
27
+ * @param entry - the key's entry.
28
+ * @param key - the key.
29
+ * @param handler - what fetches the data.
30
+ * @param options - what is done with the result.
31
+ */
32
+ export declare const startRun: (store: NuxtStore, entry: AsyncDataEntry, key: string, handler: AsyncDataHandler<unknown> | undefined, options: RunOptions) => Promise<void>;
33
+ /**
34
+ * The run a call waits for: the one under way for its key, a new one when the key has not run or failed, else none.
35
+ *
36
+ * @param entry - the key's entry.
37
+ * @param start - what starts a run.
38
+ */
39
+ export declare const waitedRun: (entry: AsyncDataEntry, start: () => Promise<void>) => Promise<void>;
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The runs of `useAsyncData`'s keys: the entry a key's calls share, and a run of its handler that a later run
3
+ * replaces.
4
+ *
5
+ * @remarks
6
+ * Runs of one key can overlap (a refresh while the first is under way, a watched value that changes twice): each run
7
+ * gets a number and its own `AbortController`, a new run aborts the one before, and a run that is no longer the latest
8
+ * drops what it got, so the data on screen is always the latest run's.
9
+ *
10
+ * @module
11
+ */
12
+ import { ref } from "vue";
13
+ import { createError } from "./nuxtError.js";
14
+ /** Whether a value is a plain object. */
15
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
16
+ /**
17
+ * The keys of a result kept, as `pick` says.
18
+ *
19
+ * @param value - the result.
20
+ * @param keys - the keys.
21
+ */
22
+ const picked = (value, keys) => keys === undefined || !isRecord(value)
23
+ ? value
24
+ : Object.fromEntries(keys.filter((key) => Object.hasOwn(value, key)).map((key) => [key, value[key]]));
25
+ /**
26
+ * The entry of a key: the one a call made before, or a new one.
27
+ *
28
+ * @param store - the story's store.
29
+ * @param key - the key.
30
+ * @param defaultData - the data a new entry starts with.
31
+ */
32
+ export const entryOf = (store, key, defaultData) => {
33
+ const known = store.asyncData.get(key);
34
+ if (known !== undefined)
35
+ return known;
36
+ const entry = {
37
+ data: ref(defaultData()),
38
+ status: ref("idle"),
39
+ error: ref(undefined),
40
+ defaultData,
41
+ running: undefined,
42
+ runId: 0,
43
+ controller: undefined,
44
+ refresh: undefined
45
+ };
46
+ store.asyncData.set(key, entry);
47
+ return entry;
48
+ };
49
+ /**
50
+ * What a handler returned, changed as the options say.
51
+ *
52
+ * @param result - what it returned.
53
+ * @param options - its `transform` and `pick`.
54
+ */
55
+ const shaped = async (result, options) => {
56
+ const transformed = options.transform === undefined ? result : await options.transform(result);
57
+ return picked(transformed, options.pick);
58
+ };
59
+ /**
60
+ * Runs a key's handler, or gives it the story's data: what it returned, transformed and picked, or the error it threw.
61
+ * A run that a later one replaced keeps nothing.
62
+ *
63
+ * @param store - the story's store, the call's own: a refresh from an event handler has no component to find it.
64
+ * @param entry - the key's entry.
65
+ * @param key - the key.
66
+ * @param handler - what fetches the data.
67
+ * @param options - what is done with the result.
68
+ */
69
+ const runHandler = async (store, entry, key, handler, options) => {
70
+ entry.runId += 1;
71
+ const id = entry.runId;
72
+ entry.controller?.abort();
73
+ const controller = new AbortController();
74
+ entry.controller = controller;
75
+ if (Object.hasOwn(store.storyData, key)) {
76
+ entry.data.value = store.storyData[key];
77
+ entry.error.value = undefined;
78
+ entry.status.value = "success";
79
+ return;
80
+ }
81
+ if (handler === undefined || store.isServer)
82
+ return;
83
+ entry.status.value = "pending";
84
+ try {
85
+ const result = await shaped(await handler(store.nuxtApp, { signal: controller.signal }), options);
86
+ if (entry.runId !== id)
87
+ return;
88
+ entry.data.value = result;
89
+ entry.error.value = undefined;
90
+ entry.status.value = "success";
91
+ }
92
+ catch (error) {
93
+ if (entry.runId !== id)
94
+ return;
95
+ entry.data.value = options.default?.() ?? entry.defaultData();
96
+ entry.error.value = createError(error);
97
+ entry.status.value = "error";
98
+ }
99
+ };
100
+ /**
101
+ * Starts a run of a key's handler, and keeps it as the run under way until it ends.
102
+ *
103
+ * @param store - the story's store.
104
+ * @param entry - the key's entry.
105
+ * @param key - the key.
106
+ * @param handler - what fetches the data.
107
+ * @param options - what is done with the result.
108
+ */
109
+ export const startRun = (store, entry, key, handler, options) => {
110
+ const running = runHandler(store, entry, key, handler, options).finally(() => {
111
+ // only the latest run clears it: an older one ending must not say nothing runs
112
+ if (entry.running === running)
113
+ entry.running = undefined;
114
+ });
115
+ entry.running = running;
116
+ return running;
117
+ };
118
+ /**
119
+ * The run a call waits for: the one under way for its key, a new one when the key has not run or failed, else none.
120
+ *
121
+ * @param entry - the key's entry.
122
+ * @param start - what starts a run.
123
+ */
124
+ export const waitedRun = (entry, start) => {
125
+ if (entry.running !== undefined)
126
+ return entry.running;
127
+ return entry.status.value === "idle" || entry.status.value === "error" ? start() : Promise.resolve();
128
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Nuxt's error, as `createError` makes it and `useError` returns it: an `Error` with an HTTP status and data.
3
+ *
4
+ * @module
5
+ */
6
+ /** What `createError` takes besides a message: Nuxt's fields, each optional. */
7
+ export type NuxtErrorInput = {
8
+ /** The message. */
9
+ readonly message?: string;
10
+ /** The HTTP status; 500 by default. */
11
+ readonly statusCode?: number;
12
+ /** The HTTP status's text. */
13
+ readonly statusMessage?: string;
14
+ /** Whether the error replaces the page with Nuxt's error page; `false` by default. */
15
+ readonly fatal?: boolean;
16
+ /** What the error carries for the page that shows it. */
17
+ readonly data?: unknown;
18
+ /** What caused it. */
19
+ readonly cause?: unknown;
20
+ };
21
+ /** An error as Nuxt makes it. */
22
+ export declare class NuxtError extends Error {
23
+ /** The HTTP status. */
24
+ readonly statusCode: number;
25
+ /** The HTTP status's text. */
26
+ readonly statusMessage: string | undefined;
27
+ /** Whether it replaces the page. */
28
+ readonly fatal: boolean;
29
+ /** What it carries. */
30
+ readonly data: unknown;
31
+ /**
32
+ * An error from Nuxt's fields.
33
+ *
34
+ * @param input - the message and the fields.
35
+ */
36
+ constructor(input: NuxtErrorInput);
37
+ }
38
+ /**
39
+ * An error as Nuxt makes it: from a message, Nuxt's fields, or another error (kept as its cause).
40
+ *
41
+ * @param input - the message, the fields or the error.
42
+ * @returns the error; a `NuxtError` given is returned as it is.
43
+ */
44
+ export declare const createError: (input: string | NuxtErrorInput | Error | unknown) => NuxtError;
45
+ /**
46
+ * Whether a value is an error Nuxt made.
47
+ *
48
+ * @param value - the value.
49
+ */
50
+ export declare const isNuxtError: (value: unknown) => value is NuxtError;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Nuxt's error, as `createError` makes it and `useError` returns it: an `Error` with an HTTP status and data.
3
+ *
4
+ * @module
5
+ */
6
+ /** An error as Nuxt makes it. */
7
+ export class NuxtError extends Error {
8
+ /** The HTTP status. */
9
+ statusCode;
10
+ /** The HTTP status's text. */
11
+ statusMessage;
12
+ /** Whether it replaces the page. */
13
+ fatal;
14
+ /** What it carries. */
15
+ data;
16
+ /**
17
+ * An error from Nuxt's fields.
18
+ *
19
+ * @param input - the message and the fields.
20
+ */
21
+ constructor(input) {
22
+ super(input.message ?? input.statusMessage ?? "", { cause: input.cause });
23
+ this.name = "NuxtError";
24
+ this.statusCode = input.statusCode ?? 500;
25
+ this.statusMessage = input.statusMessage;
26
+ this.fatal = input.fatal ?? false;
27
+ this.data = input.data;
28
+ }
29
+ }
30
+ /** Whether a value is a plain object. */
31
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
32
+ /**
33
+ * Nuxt's fields read from an error another library threw (a fetch's `FetchError` has `statusCode` and `data`).
34
+ *
35
+ * @param value - the error.
36
+ */
37
+ const fieldsOf = (value) => ({
38
+ ...(typeof value["message"] === "string" ? { message: value["message"] } : {}),
39
+ ...(typeof value["statusCode"] === "number" ? { statusCode: value["statusCode"] } : {}),
40
+ ...(typeof value["statusMessage"] === "string" ? { statusMessage: value["statusMessage"] } : {}),
41
+ ...(typeof value["fatal"] === "boolean" ? { fatal: value["fatal"] } : {}),
42
+ ...("data" in value ? { data: value["data"] } : {})
43
+ });
44
+ /**
45
+ * An error as Nuxt makes it: from a message, Nuxt's fields, or another error (kept as its cause).
46
+ *
47
+ * @param input - the message, the fields or the error.
48
+ * @returns the error; a `NuxtError` given is returned as it is.
49
+ */
50
+ export const createError = (input) => {
51
+ if (input instanceof NuxtError)
52
+ return input;
53
+ if (typeof input === "string")
54
+ return new NuxtError({ message: input });
55
+ // read field by field: a fetch error's status is a getter, which a spread would not copy
56
+ if (input instanceof Error && isRecord(input))
57
+ return new NuxtError({ ...fieldsOf(input), cause: input });
58
+ return new NuxtError(isRecord(input) ? fieldsOf(input) : { message: String(input) });
59
+ };
60
+ /**
61
+ * Whether a value is an error Nuxt made.
62
+ *
63
+ * @param value - the value.
64
+ */
65
+ export const isNuxtError = (value) => value instanceof NuxtError;
@@ -0,0 +1,14 @@
1
+ import type { StoryRenderer } from "forgebook/renderer";
2
+ import type { NuxtProject } from "./project.ts";
3
+ /**
4
+ * Forgebook's renderer for a Nuxt project.
5
+ *
6
+ * @remarks
7
+ * The apps of one story drawn at once (the cells of a matrix) share its store, so an event handler, which has no
8
+ * component to find its store by, moves the same router whichever cell it is in; once the last of them is taken away,
9
+ * the story drawn anew starts over with a store of its own.
10
+ *
11
+ * @param project - what the page knows of the project: its routes, config and components.
12
+ * @returns the renderer.
13
+ */
14
+ export declare const createNuxtRenderer: (project: NuxtProject) => StoryRenderer;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Forgebook's renderer for a Nuxt project: Vue's renderer, each story's app given its Nuxt (`nuxtStore.ts`): its router
3
+ * at the story's route, its data, state and config, Nuxt's components and the project's.
4
+ *
5
+ * @remarks
6
+ * A story's Nuxt is made when its app is, so a story drawn again with other args keeps where its links took it, and
7
+ * one drawn anew starts over at its own route. Its calls go to the catalogue's Events tab. The project's components are
8
+ * registered on the app too, loaded when first drawn, so a story's own template (`template: "<BaseButton />"`) finds
9
+ * them as a Nuxt page's does; a component file finds them through the plugin, which imports what its template names.
10
+ *
11
+ * @module
12
+ */
13
+ import { createVueRenderer } from "@forgebook/vue/renderer";
14
+ import { defineAsyncComponent } from "vue";
15
+ import { BUILT_IN_COMPONENTS } from "./app/components.js";
16
+ import { createNuxtStore, setCurrentStore } from "./nuxtStore.js";
17
+ /**
18
+ * Forgebook's renderer for a Nuxt project.
19
+ *
20
+ * @remarks
21
+ * The apps of one story drawn at once (the cells of a matrix) share its store, so an event handler, which has no
22
+ * component to find its store by, moves the same router whichever cell it is in; once the last of them is taken away,
23
+ * the story drawn anew starts over with a store of its own.
24
+ *
25
+ * @param project - what the page knows of the project: its routes, config and components.
26
+ * @returns the renderer.
27
+ */
28
+ export const createNuxtRenderer = (project) => {
29
+ const shared = new Map();
30
+ const storeFor = (drawing, place) => {
31
+ // a server render is never taken away, so each has a store of its own
32
+ if (place === "server")
33
+ return { store: createNuxtStore(project, drawing.story.parameters, drawing.onEvent, place), apps: 0 };
34
+ const id = drawing.identity.id;
35
+ const known = shared.get(id);
36
+ if (known !== undefined)
37
+ return known;
38
+ const created = {
39
+ store: createNuxtStore(project, drawing.story.parameters, drawing.onEvent, place),
40
+ apps: 0
41
+ };
42
+ shared.set(id, created);
43
+ return created;
44
+ };
45
+ return createVueRenderer({
46
+ prepareApp: (app, drawing, place) => {
47
+ const entry = storeFor(drawing, place);
48
+ entry.apps += 1;
49
+ const { unmount } = app;
50
+ app.unmount = () => {
51
+ unmount();
52
+ entry.apps -= 1;
53
+ if (entry.apps === 0 && shared.get(drawing.identity.id) === entry)
54
+ shared.delete(drawing.identity.id);
55
+ };
56
+ setCurrentStore(entry.store);
57
+ entry.store.install(app);
58
+ for (const [name, component] of Object.entries(BUILT_IN_COMPONENTS))
59
+ app.component(name, component);
60
+ for (const [name, load] of Object.entries(project.components)) {
61
+ const component = defineAsyncComponent(load);
62
+ app.component(name, component);
63
+ app.component(`Lazy${name}`, component);
64
+ }
65
+ }
66
+ });
67
+ };