@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.
Files changed (103) hide show
  1. package/README.md +9 -1
  2. package/dist/cancellation.js +2 -1
  3. package/dist/config.js +11 -7
  4. package/dist/derived-outputs.js +6 -113
  5. package/dist/dev-modules.js +3 -2
  6. package/dist/discovery.js +19 -12
  7. package/dist/fs-sync.js +1 -2
  8. package/dist/internal/guards.d.ts +5 -0
  9. package/dist/internal/guards.js +22 -0
  10. package/dist/internal/locale-catalogs.d.ts +6 -0
  11. package/dist/internal/locale-catalogs.js +115 -0
  12. package/dist/layer-aliases.js +2 -1
  13. package/dist/layers.js +2 -1
  14. package/dist/locale-files.js +9 -4
  15. package/dist/manifest-secrets.js +11 -4
  16. package/dist/manifest.js +11 -5
  17. package/dist/materialize.js +6 -5
  18. package/dist/module-set.js +2 -4
  19. package/dist/ports.js +8 -1
  20. package/dist/private-values.js +26 -17
  21. package/dist/project-modules.js +17 -11
  22. package/dist/renderer-range.js +2 -1
  23. package/dist/server-process.js +17 -16
  24. package/dist/update-check.js +23 -16
  25. package/dist/verify-source-runner.js +2 -2
  26. package/dist/workspace-setup.js +1 -2
  27. package/dist/workspace.js +11 -10
  28. package/package.json +7 -4
  29. package/skills/dms-frontend/SKILL.md +1 -1
  30. package/skills/dms-frontend/cases.json +77 -0
  31. package/templates/vue/app-runtime.ts +88 -7
  32. package/templates/vue/client-chunking.mjs +0 -5
  33. package/templates/vue/client-server-guard.mjs +10 -13
  34. package/templates/vue/entry-weight.mjs +12 -14
  35. package/templates/vue/frontend-module.ts +159 -3563
  36. package/templates/vue/frontend-runtime/async-data-entry.ts +320 -0
  37. package/templates/vue/frontend-runtime/async-data.ts +404 -0
  38. package/templates/vue/frontend-runtime/components.ts +386 -0
  39. package/templates/vue/frontend-runtime/cookies.ts +125 -0
  40. package/templates/vue/frontend-runtime/data-access.ts +132 -0
  41. package/templates/vue/frontend-runtime/dms-error.ts +11 -0
  42. package/templates/vue/frontend-runtime/errors.ts +145 -0
  43. package/templates/vue/frontend-runtime/fetch.ts +306 -0
  44. package/templates/vue/frontend-runtime/module-registration.ts +247 -0
  45. package/templates/vue/frontend-runtime/paged-data.ts +191 -0
  46. package/templates/vue/frontend-runtime/payload.ts +172 -0
  47. package/templates/vue/frontend-runtime/route.ts +328 -0
  48. package/templates/vue/frontend-runtime/sdk.ts +300 -0
  49. package/templates/vue/frontend-runtime/state.ts +167 -0
  50. package/templates/vue/frontend-runtime/types.ts +361 -0
  51. package/templates/vue/frontend-runtime/ui.ts +247 -0
  52. package/templates/vue/globals.d.ts +5 -0
  53. package/templates/vue/head-order.mjs +11 -13
  54. package/templates/vue/icon-bundle.ts +31 -0
  55. package/templates/vue/message-subset.ts +65 -0
  56. package/templates/vue/network-error.ts +4 -0
  57. package/templates/vue/prepare-types.mjs +0 -8
  58. package/templates/vue/server/auth/backend.mjs +4 -2
  59. package/templates/vue/server/auth/establish-endpoints.mjs +14 -9
  60. package/templates/vue/server/auth/session.mjs +4 -3
  61. package/templates/vue/server/auth-hooks.mjs +6 -6
  62. package/templates/vue/server/backend-proxies.mjs +6 -6
  63. package/templates/vue/server/backend-response.mjs +15 -14
  64. package/templates/vue/server/built-files.mjs +10 -8
  65. package/templates/vue/server/cli-report.mjs +7 -5
  66. package/templates/vue/server/client-error.mjs +11 -8
  67. package/templates/vue/server/client-manifest.mjs +9 -8
  68. package/templates/vue/server/content.mjs +2 -1
  69. package/templates/vue/server/dev-private-files.mjs +20 -15
  70. package/templates/vue/server/dev-styles.mjs +22 -25
  71. package/templates/vue/server/email-bundle-watch.mjs +0 -12
  72. package/templates/vue/server/email.mjs +20 -15
  73. package/templates/vue/server/favicon.mjs +12 -11
  74. package/templates/vue/server/homepage.mjs +8 -10
  75. package/templates/vue/server/icons.mjs +18 -9
  76. package/templates/vue/server/in-place-render.mjs +6 -8
  77. package/templates/vue/server/listen-host.mjs +2 -2
  78. package/templates/vue/server/module-event.mjs +5 -5
  79. package/templates/vue/server/module-routes.mjs +11 -7
  80. package/templates/vue/server/module-server.mjs +8 -7
  81. package/templates/vue/server/module-testing.mjs +7 -7
  82. package/templates/vue/server/page-props.mjs +14 -14
  83. package/templates/vue/server/path-patterns.mjs +4 -4
  84. package/templates/vue/server/proxy-compression.mjs +21 -13
  85. package/templates/vue/server/proxy-headers.mjs +5 -3
  86. package/templates/vue/server/proxy-redirects.mjs +7 -6
  87. package/templates/vue/server/proxy-transfers.mjs +15 -11
  88. package/templates/vue/server/public-endpoints.mjs +27 -19
  89. package/templates/vue/server/public-options.mjs +9 -7
  90. package/templates/vue/server/request-failure.mjs +4 -6
  91. package/templates/vue/server/request-routing.mjs +8 -11
  92. package/templates/vue/server/request-target.mjs +6 -6
  93. package/templates/vue/server/route-rules.mjs +7 -5
  94. package/templates/vue/server/static-files.mjs +19 -10
  95. package/templates/vue/server/web-body.mjs +3 -4
  96. package/templates/vue/server.mjs +32 -19
  97. package/templates/vue/sfc-global-types.mjs +27 -24
  98. package/templates/vue/ssr-renderer.ts +10 -1
  99. package/templates/vue/ui-inertia-page.mjs +7 -7
  100. package/templates/vue/ui-kbd.mjs +10 -10
  101. package/templates/vue/ui-lazy-icons.mjs +44 -0
  102. package/templates/vue/ui-request-state.mjs +9 -9
  103. package/templates/vue/vite.config.ts +2 -0
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  <a href="https://www.npmjs.com/package/@antelopejs/dms-frontend"><img alt="npm" src="https://img.shields.io/npm/v/@antelopejs/dms-frontend?style=for-the-badge&labelColor=000000&color=000000"></a>
11
11
  <a href="./LICENSE"><img alt="License" src="https://img.shields.io/npm/l/@antelopejs/dms-frontend?style=for-the-badge&labelColor=000000&color=000000"></a>
12
- <a href="https://discord.gg/sjK28QHrA7"><img alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
12
+ <a href="https://discord.gg/g4pFt6eXqu"><img alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
13
13
  <a href="https://dms.antelopejs.com/docs/frontend"><img alt="Documentation" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000"></a>
14
14
 
15
15
  **The frontend of the AntelopeJS DMS: the `ajs dms` CLI, the Vue renderer and the frontend-module SDK.**
@@ -55,6 +55,14 @@ pnpm exec ajs dms start -b https://api.example.com -p 3001
55
55
 
56
56
  ## Compatibility
57
57
 
58
+ <!-- antelopejs-docs-compat:start -->
59
+
60
+ | Package | Supported | Tested with |
61
+ | ------------------ | ------------- | ----------- |
62
+ | `@antelopejs/core` | `>=1.16.0 <2` | `1.16.0` |
63
+
64
+ <!-- antelopejs-docs-compat:end -->
65
+
58
66
  | `@antelopejs/dms` | `@antelopejs/dms-frontend` |
59
67
  | --- | --- |
60
68
  | 0.9.1 and later | `>=0.6.1 <0.7.0` |
@@ -4,9 +4,10 @@ exports.CancelledError = void 0;
4
4
  exports.exitCodeForSignal = exitCodeForSignal;
5
5
  const node_os_1 = require("node:os");
6
6
  const cli_1 = require("@antelopejs/core/cli");
7
+ const guards_1 = require("./internal/guards");
7
8
  function exitCodeForSignal(signal) {
8
9
  const number = node_os_1.constants.signals[signal];
9
- return 128 + (typeof number === "number" ? number : 15);
10
+ return 128 + ((0, guards_1.isNumber)(number) ? number : 15);
10
11
  }
11
12
  class CancelledError extends Error {
12
13
  constructor(signal, stopped = "Stopped", context) {
package/dist/config.js CHANGED
@@ -50,15 +50,18 @@ exports.TEMPLATE_FILES = [
50
50
  ["index.html", "index.html"],
51
51
  ["main.ts", "main.ts"],
52
52
  ["app-runtime.ts", "app-runtime.ts"],
53
+ ["message-subset.ts", "message-subset.ts"],
53
54
  ["network-error.ts", "network-error.ts"],
54
55
  ["ui-app-config.ts", "ui-app-config.ts"],
55
56
  ["layer-app-config.ts", "layer-app-config.ts"],
56
57
  ["ssr-renderer.ts", "ssr-renderer.ts"],
57
58
  ["frontend-module.ts", "frontend-module.ts"],
59
+ ["frontend-runtime", "frontend-runtime"],
58
60
  ["frontend-build.ts", "frontend-build.ts"],
59
61
  ["frontend-build-loader.ts", "frontend-build-loader.ts"],
60
62
  ["server-module.ts", "server-module.ts"],
61
63
  ["icon-hydration.ts", "icon-hydration.ts"],
64
+ ["icon-bundle.ts", "icon-bundle.ts"],
62
65
  ["globals.d.ts", "globals.d.ts"],
63
66
  ["compress-assets.mjs", "compress-assets.mjs"],
64
67
  ["head-order.mjs", "head-order.mjs"],
@@ -71,6 +74,7 @@ exports.TEMPLATE_FILES = [
71
74
  ["icon-api.mjs", "icon-api.mjs"],
72
75
  ["icon-scan.mjs", "icon-scan.mjs"],
73
76
  ["ui-kbd.mjs", "ui-kbd.mjs"],
77
+ ["ui-lazy-icons.mjs", "ui-lazy-icons.mjs"],
74
78
  ["email-renderer.ts", "email-renderer.ts"],
75
79
  ["email-runtime.ts", "email-runtime.ts"],
76
80
  ["email-locales.ts", "email-locales.ts"],
@@ -202,16 +206,16 @@ exports.Options = {
202
206
  };
203
207
  const HEADER_SAFE_CREDENTIAL = /^[\x21-\x7e]+$/;
204
208
  const HEADER_UNSAFE_CHARACTER = /[^\x21-\x7e]/u;
205
- const UNSAFE_CHARACTER_NAMES = {
206
- " ": "a space",
207
- "\t": "a tab",
208
- "\r": "a line break",
209
- "\n": "a line break",
210
- };
209
+ const UNSAFE_CHARACTER_NAMES = new Map([
210
+ [" ", "a space"],
211
+ ["\t", "a tab"],
212
+ ["\r", "a line break"],
213
+ ["\n", "a line break"],
214
+ ]);
211
215
  const LAST_ASCII_CODE_POINT = 0x7f;
212
216
  function describeUnsafeCharacter(value) {
213
217
  const char = HEADER_UNSAFE_CHARACTER.exec(value)?.[0] ?? "";
214
- const named = UNSAFE_CHARACTER_NAMES[char];
218
+ const named = UNSAFE_CHARACTER_NAMES.get(char);
215
219
  if (named)
216
220
  return named;
217
221
  return (char.codePointAt(0) ?? 0) > LAST_ASCII_CODE_POINT
@@ -14,6 +14,7 @@ const layer_aliases_1 = require("./layer-aliases");
14
14
  const layers_1 = require("./layers");
15
15
  const project_modules_1 = require("./project-modules");
16
16
  const fs_sync_1 = require("./fs-sync");
17
+ const locale_catalogs_1 = require("./internal/locale-catalogs");
17
18
  const GENERATED_MODULE_ID = /^[A-Za-z0-9_-]+$/;
18
19
  function validateFrontendModuleRegistry(registry, workspaceDir) {
19
20
  const ids = new Set();
@@ -136,20 +137,6 @@ function discoverAssets(registry, directory, extensions) {
136
137
  });
137
138
  });
138
139
  }
139
- function isRecord(value) {
140
- return typeof value === "object" && value !== null && !Array.isArray(value);
141
- }
142
- function mergeLocaleMessages(source, existing) {
143
- const merged = { ...source };
144
- for (const [key, value] of Object.entries(existing)) {
145
- const sourceValue = merged[key];
146
- merged[key] =
147
- isRecord(sourceValue) && isRecord(value)
148
- ? mergeLocaleMessages(sourceValue, value)
149
- : value;
150
- }
151
- return merged;
152
- }
153
140
  const LOCALE_DIRECTORY = "i18n/locales";
154
141
  function readLocaleMessages(assets, registry) {
155
142
  const roots = new Map(registry.modules.map((module) => [module.id, module.root]));
@@ -160,107 +147,13 @@ function readLocaleMessages(assets, registry) {
160
147
  if (!locale || !root)
161
148
  continue;
162
149
  const value = (0, locale_files_1.readLocaleFile)((0, node_path_1.join)(root, asset.relativePath));
163
- messages[locale] = mergeLocaleMessages(value, messages[locale] ?? {});
150
+ messages[locale] = (0, locale_catalogs_1.mergeLocaleMessages)(value, messages[locale] ?? {});
164
151
  }
165
152
  return messages;
166
153
  }
167
154
  function affectsLocaleMessages(relativePath) {
168
155
  return (0, asset_roots_1.affectsAssetDirectory)(LOCALE_DIRECTORY, relativePath);
169
156
  }
170
- const FALLBACK_LOCALE = "en";
171
- const RENDERER_TRANSLATED_KEYS = ["error.network"];
172
- function withoutMessage(messages, key) {
173
- const [head, ...rest] = key.split(".");
174
- if (!(head in messages))
175
- return messages;
176
- const { [head]: value, ...others } = messages;
177
- if (!rest.length)
178
- return others;
179
- if (!isRecord(value))
180
- return messages;
181
- const remaining = withoutMessage(value, rest.join("."));
182
- return Object.keys(remaining).length
183
- ? { ...others, [head]: remaining }
184
- : others;
185
- }
186
- function localeModuleSource(supportedLocales) {
187
- const loaders = supportedLocales.map((locale) => ` ${JSON.stringify(locale)}: () => import(${JSON.stringify(`./locales.generated/${locale}`)}),`);
188
- const fallback = supportedLocales.includes(FALLBACK_LOCALE)
189
- ? FALLBACK_LOCALE
190
- : supportedLocales[0];
191
- return `type LocaleMessages = Record<string, unknown>;
192
- type LocaleLoader = () => Promise<{ default: LocaleMessages }>;
193
- interface LocaleMessagesTarget {
194
- setLocaleMessage(locale: string, messages: LocaleMessages): void;
195
- }
196
- interface LocaleState {
197
- messages: Record<string, LocaleMessages>;
198
- targets: Set<LocaleMessagesTarget>;
199
- version: number;
200
- }
201
-
202
- export const supportedLocales = ${JSON.stringify(supportedLocales)};
203
- const fallbackLocale: string | undefined = ${JSON.stringify(fallback)};
204
- const localeLoaders: Record<string, LocaleLoader> = {
205
- ${loaders.join("\n")}
206
- };
207
- const shared: LocaleState | undefined = import.meta.hot?.data.state;
208
- const state: LocaleState = shared ?? {
209
- messages: {},
210
- targets: new Set(),
211
- version: 0,
212
- };
213
- export const localeMessages = state.messages;
214
-
215
- async function readLocaleMessages(locale: string): Promise<LocaleMessages> {
216
- return (await localeLoaders[locale]()).default;
217
- }
218
-
219
- /**
220
- * The catalog that serves \`locale\`: its own when the application has one,
221
- * else the fallback locale's.
222
- */
223
- export function catalogLocale(locale: string): string | undefined {
224
- const normalized = locale.slice(0, 2);
225
- return normalized in localeLoaders ? normalized : fallbackLocale;
226
- }
227
-
228
- export async function loadLocaleMessages(locale: string): Promise<LocaleMessages> {
229
- const catalog = catalogLocale(locale);
230
- if (!catalog) return {};
231
- localeMessages[catalog] ??= await readLocaleMessages(catalog);
232
- return localeMessages[catalog];
233
- }
234
-
235
- /** Development only: applies every regenerated catalog to \`target\`. */
236
- export function syncLocaleMessages(target: LocaleMessagesTarget): void {
237
- if (import.meta.hot) state.targets.add(target);
238
- }
239
-
240
- async function refreshLocaleMessages(): Promise<void> {
241
- const version = ++state.version;
242
- const locales = Object.keys(localeMessages).filter(
243
- (locale) => locale in localeLoaders,
244
- );
245
- const catalogs = await Promise.all(locales.map(readLocaleMessages));
246
- if (version !== state.version) return;
247
- locales.forEach((locale, index) => {
248
- localeMessages[locale] = catalogs[index];
249
- for (const target of state.targets)
250
- target.setLocaleMessage(locale, catalogs[index]);
251
- });
252
- }
253
-
254
- if (import.meta.hot) {
255
- import.meta.hot.data.state = state;
256
- if (shared) void refreshLocaleMessages();
257
- import.meta.hot.accept((next) => {
258
- if (next?.supportedLocales.join() !== supportedLocales.join())
259
- import.meta.hot?.invalidate("the list of locales changed");
260
- });
261
- }
262
- `;
263
- }
264
157
  function writeLocaleMessages(workspaceDir, registry) {
265
158
  const locales = discoverAssets(registry, LOCALE_DIRECTORY, [".json"]);
266
159
  const messages = readLocaleMessages(locales, registry);
@@ -271,19 +164,19 @@ function writeLocaleMessages(workspaceDir, registry) {
271
164
  changed = (0, fs_sync_1.applyContent)(content, path) || changed;
272
165
  };
273
166
  (0, node_fs_1.mkdirSync)(outputDir, { recursive: true });
274
- const fallback = RENDERER_TRANSLATED_KEYS.reduce(withoutMessage, messages[FALLBACK_LOCALE] ?? {});
167
+ const fallback = locale_catalogs_1.RENDERER_TRANSLATED_KEYS.reduce(locale_catalogs_1.withoutMessage, messages[locale_catalogs_1.FALLBACK_LOCALE] ?? {});
275
168
  for (const [locale, value] of Object.entries(messages)) {
276
169
  const catalog = JSON.stringify(value);
277
- const complete = locale === FALLBACK_LOCALE
170
+ const complete = locale === locale_catalogs_1.FALLBACK_LOCALE
278
171
  ? catalog
279
- : JSON.stringify(mergeLocaleMessages(fallback, value));
172
+ : JSON.stringify((0, locale_catalogs_1.mergeLocaleMessages)(fallback, value));
280
173
  apply(catalog, (0, node_path_1.join)(outputDir, `${locale}.json`));
281
174
  apply(`const messages = ${complete};\nexport default messages;\n`, (0, node_path_1.join)(outputDir, `${locale}.ts`));
282
175
  outputs.add(`${locale}.json`).add(`${locale}.ts`);
283
176
  }
284
177
  const supportedLocales = Object.keys(messages);
285
178
  apply(JSON.stringify(supportedLocales), (0, node_path_1.join)(workspaceDir, "email-locales.generated.json"));
286
- apply(localeModuleSource(supportedLocales), (0, node_path_1.join)(workspaceDir, "locales.generated.ts"));
179
+ apply((0, locale_catalogs_1.localeModuleSource)(supportedLocales), (0, node_path_1.join)(workspaceDir, "locales.generated.ts"));
287
180
  for (const name of (0, node_fs_1.readdirSync)(outputDir)) {
288
181
  if (outputs.has(name))
289
182
  continue;
@@ -6,6 +6,7 @@ exports.withBackendValues = withBackendValues;
6
6
  const node_child_process_1 = require("node:child_process");
7
7
  const cli_1 = require("@antelopejs/core/cli");
8
8
  const private_values_1 = require("./private-values");
9
+ const guards_1 = require("./internal/guards");
9
10
  function readProjectModulesFresh(options) {
10
11
  return new Promise((resolve, reject) => {
11
12
  const child = (0, node_child_process_1.fork)(require.resolve("./project-modules-child"), [], {
@@ -48,11 +49,11 @@ function rewriteOrigins(options, rewrites) {
48
49
  .join("|");
49
50
  const pattern = new RegExp(`(?:${alternation})${URL_BOUNDARY}`, "g");
50
51
  const rewrite = (value) => {
51
- if (typeof value === "string")
52
+ if ((0, guards_1.isText)(value))
52
53
  return value.replace(pattern, (match) => replacements.get(match) ?? match);
53
54
  if (Array.isArray(value))
54
55
  return value.map(rewrite);
55
- if (value !== null && typeof value === "object")
56
+ if ((0, guards_1.isObjectLike)(value))
56
57
  return rewriteObject(value);
57
58
  return value;
58
59
  };
package/dist/discovery.js CHANGED
@@ -9,6 +9,7 @@ exports.readDevBootstrapCredential = readDevBootstrapCredential;
9
9
  const node_fs_1 = require("node:fs");
10
10
  const node_path_1 = require("node:path");
11
11
  const output_1 = require("./output");
12
+ const guards_1 = require("./internal/guards");
12
13
  exports.DEV_REGISTRY_RELATIVE_PATH = (0, node_path_1.join)(".antelope", "dev.json");
13
14
  function isPidAlive(pid) {
14
15
  if (!Number.isInteger(pid) || pid <= 0)
@@ -21,15 +22,18 @@ function isPidAlive(pid) {
21
22
  return err?.code === "EPERM";
22
23
  }
23
24
  }
25
+ function isDevRegistry(value) {
26
+ return ((0, guards_1.isObjectLike)(value) &&
27
+ "pid" in value &&
28
+ typeof value.pid === "number" &&
29
+ "servers" in value &&
30
+ typeof value.servers === "object" &&
31
+ value.servers !== null);
32
+ }
24
33
  function parseRegistry(file) {
25
34
  try {
26
35
  const parsed = JSON.parse((0, node_fs_1.readFileSync)(file, "utf-8"));
27
- if (typeof parsed?.pid !== "number" ||
28
- typeof parsed?.servers !== "object" ||
29
- parsed.servers === null) {
30
- return undefined;
31
- }
32
- return parsed;
36
+ return isDevRegistry(parsed) ? parsed : undefined;
33
37
  }
34
38
  catch {
35
39
  return undefined;
@@ -114,15 +118,18 @@ function describeDiscoveryFailure(result) {
114
118
  }
115
119
  }
116
120
  exports.DEV_HANDSHAKE_RELATIVE_PATH = (0, node_path_1.join)(".antelope", "dms-dev.json");
121
+ function isDevHandshake(value) {
122
+ return ((0, guards_1.isObjectLike)(value) &&
123
+ "pid" in value &&
124
+ typeof value.pid === "number" &&
125
+ "bootstrapSecret" in value &&
126
+ typeof value.bootstrapSecret === "string" &&
127
+ value.bootstrapSecret !== "");
128
+ }
117
129
  function parseHandshake(file) {
118
130
  try {
119
131
  const parsed = JSON.parse((0, node_fs_1.readFileSync)(file, "utf-8"));
120
- if (typeof parsed?.pid !== "number" ||
121
- typeof parsed?.bootstrapSecret !== "string" ||
122
- !parsed.bootstrapSecret) {
123
- return undefined;
124
- }
125
- return parsed;
132
+ return isDevHandshake(parsed) ? parsed : undefined;
126
133
  }
127
134
  catch {
128
135
  return undefined;
package/dist/fs-sync.js CHANGED
@@ -68,8 +68,7 @@ function applyContent(content, destPath) {
68
68
  if ((0, node_fs_1.readFileSync)(destPath).equals(buf))
69
69
  return false;
70
70
  }
71
- catch {
72
- }
71
+ catch { }
73
72
  }
74
73
  (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(destPath), { recursive: true });
75
74
  const tmp = `${destPath}.ajs-dms-tmp-${process.pid}-${Date.now()}`;
@@ -0,0 +1,5 @@
1
+ export declare function isText(value: unknown): value is string;
2
+ export declare function isNumber(value: unknown): value is number;
3
+ export declare function isFlag(value: unknown): value is boolean;
4
+ export declare function isCallable(value: unknown): value is (...args: never[]) => void;
5
+ export declare function isObjectLike(value: unknown): value is object;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isText = isText;
4
+ exports.isNumber = isNumber;
5
+ exports.isFlag = isFlag;
6
+ exports.isCallable = isCallable;
7
+ exports.isObjectLike = isObjectLike;
8
+ function isText(value) {
9
+ return typeof value === "string";
10
+ }
11
+ function isNumber(value) {
12
+ return typeof value === "number";
13
+ }
14
+ function isFlag(value) {
15
+ return typeof value === "boolean";
16
+ }
17
+ function isCallable(value) {
18
+ return typeof value === "function";
19
+ }
20
+ function isObjectLike(value) {
21
+ return typeof value === "object" && value !== null;
22
+ }
@@ -0,0 +1,6 @@
1
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
2
+ export declare function mergeLocaleMessages(source: Record<string, unknown>, existing: Record<string, unknown>): Record<string, unknown>;
3
+ export declare const FALLBACK_LOCALE = "en";
4
+ export declare const RENDERER_TRANSLATED_KEYS: string[];
5
+ export declare function withoutMessage(messages: Record<string, unknown>, key: string): Record<string, unknown>;
6
+ export declare function localeModuleSource(supportedLocales: string[]): string;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RENDERER_TRANSLATED_KEYS = exports.FALLBACK_LOCALE = void 0;
4
+ exports.isRecord = isRecord;
5
+ exports.mergeLocaleMessages = mergeLocaleMessages;
6
+ exports.withoutMessage = withoutMessage;
7
+ exports.localeModuleSource = localeModuleSource;
8
+ function isRecord(value) {
9
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10
+ }
11
+ function mergeLocaleMessages(source, existing) {
12
+ const merged = { ...source };
13
+ for (const [key, value] of Object.entries(existing)) {
14
+ const sourceValue = merged[key];
15
+ merged[key] =
16
+ isRecord(sourceValue) && isRecord(value)
17
+ ? mergeLocaleMessages(sourceValue, value)
18
+ : value;
19
+ }
20
+ return merged;
21
+ }
22
+ exports.FALLBACK_LOCALE = "en";
23
+ exports.RENDERER_TRANSLATED_KEYS = ["error.network"];
24
+ function withoutMessage(messages, key) {
25
+ const [head, ...rest] = key.split(".");
26
+ if (!(head in messages))
27
+ return messages;
28
+ const { [head]: value, ...others } = messages;
29
+ if (!rest.length)
30
+ return others;
31
+ if (!isRecord(value))
32
+ return messages;
33
+ const remaining = withoutMessage(value, rest.join("."));
34
+ return Object.keys(remaining).length
35
+ ? { ...others, [head]: remaining }
36
+ : others;
37
+ }
38
+ function localeModuleSource(supportedLocales) {
39
+ const loaders = supportedLocales.map((locale) => ` ${JSON.stringify(locale)}: () => import(${JSON.stringify(`./locales.generated/${locale}`)}),`);
40
+ const fallback = supportedLocales.includes(exports.FALLBACK_LOCALE)
41
+ ? exports.FALLBACK_LOCALE
42
+ : supportedLocales[0];
43
+ return `type LocaleMessages = Record<string, unknown>;
44
+ type LocaleLoader = () => Promise<{ default: LocaleMessages }>;
45
+ interface LocaleMessagesTarget {
46
+ setLocaleMessage(locale: string, messages: LocaleMessages): void;
47
+ }
48
+ interface LocaleState {
49
+ messages: Record<string, LocaleMessages>;
50
+ targets: Set<LocaleMessagesTarget>;
51
+ version: number;
52
+ }
53
+
54
+ export const supportedLocales = ${JSON.stringify(supportedLocales)};
55
+ const fallbackLocale: string | undefined = ${JSON.stringify(fallback)};
56
+ const localeLoaders: Record<string, LocaleLoader> = {
57
+ ${loaders.join("\n")}
58
+ };
59
+ const shared: LocaleState | undefined = import.meta.hot?.data.state;
60
+ const state: LocaleState = shared ?? {
61
+ messages: {},
62
+ targets: new Set(),
63
+ version: 0,
64
+ };
65
+ export const localeMessages = state.messages;
66
+
67
+ async function readLocaleMessages(locale: string): Promise<LocaleMessages> {
68
+ return (await localeLoaders[locale]()).default;
69
+ }
70
+
71
+ /**
72
+ * The catalog that serves \`locale\`: its own when the application has one,
73
+ * else the fallback locale's.
74
+ */
75
+ export function catalogLocale(locale: string): string | undefined {
76
+ const normalized = locale.slice(0, 2);
77
+ return normalized in localeLoaders ? normalized : fallbackLocale;
78
+ }
79
+
80
+ export async function loadLocaleMessages(locale: string): Promise<LocaleMessages> {
81
+ const catalog = catalogLocale(locale);
82
+ if (!catalog) return {};
83
+ localeMessages[catalog] ??= await readLocaleMessages(catalog);
84
+ return localeMessages[catalog];
85
+ }
86
+
87
+ /** Development only: applies every regenerated catalog to \`target\`. */
88
+ export function syncLocaleMessages(target: LocaleMessagesTarget): void {
89
+ if (import.meta.hot) state.targets.add(target);
90
+ }
91
+
92
+ async function refreshLocaleMessages(): Promise<void> {
93
+ const version = ++state.version;
94
+ const locales = Object.keys(localeMessages).filter(
95
+ (locale) => locale in localeLoaders,
96
+ );
97
+ const catalogs = await Promise.all(locales.map(readLocaleMessages));
98
+ if (version !== state.version) return;
99
+ locales.forEach((locale, index) => {
100
+ localeMessages[locale] = catalogs[index];
101
+ for (const target of state.targets)
102
+ target.setLocaleMessage(locale, catalogs[index]);
103
+ });
104
+ }
105
+
106
+ if (import.meta.hot) {
107
+ import.meta.hot.data.state = state;
108
+ if (shared) void refreshLocaleMessages();
109
+ import.meta.hot.accept((next) => {
110
+ if (next?.supportedLocales.join() !== supportedLocales.join())
111
+ import.meta.hot?.invalidate("the list of locales changed");
112
+ });
113
+ }
114
+ `;
115
+ }
@@ -7,6 +7,7 @@ exports.frontendLayerTypePaths = frontendLayerTypePaths;
7
7
  const node_fs_1 = require("node:fs");
8
8
  const node_path_1 = require("node:path");
9
9
  const fs_sync_1 = require("./fs-sync");
10
+ const guards_1 = require("./internal/guards");
10
11
  exports.MODULE_LAYERS_DIRNAME = "layers";
11
12
  const LAYER_ALIAS_PREFIX = "#";
12
13
  function isDirectory(path) {
@@ -30,7 +31,7 @@ function readModuleLayers(moduleRoot) {
30
31
  .filter((layer) => isDirectory(layer.directory));
31
32
  }
32
33
  function resolveFrontendLayers(moduleRoots) {
33
- const roots = typeof moduleRoots === "string" ? [moduleRoots] : moduleRoots;
34
+ const roots = (0, guards_1.isText)(moduleRoots) ? [moduleRoots] : moduleRoots;
34
35
  const layers = new Map();
35
36
  for (const root of roots) {
36
37
  for (const layer of readModuleLayers(root)) {
package/dist/layers.js CHANGED
@@ -11,6 +11,7 @@ const cli_1 = require("@antelopejs/core/cli");
11
11
  const manifest_1 = require("./manifest");
12
12
  const config_1 = require("./config");
13
13
  const output_1 = require("./output");
14
+ const guards_1 = require("./internal/guards");
14
15
  const BUILD_FIX = "A remote or production backend never sends them: build the frontend from the project instead, with `ajs dms build`";
15
16
  function assertLayerPathsServed(modules, backendUrl, { bootstrapSecret, cache } = {}) {
16
17
  const pathless = modules.filter((mod) => !mod.path);
@@ -63,7 +64,7 @@ function readLayerPackage(layerPath) {
63
64
  }
64
65
  function readLayerPackageName(layerPath) {
65
66
  const name = readLayerPackage(layerPath)?.name;
66
- return typeof name === "string" ? name : undefined;
67
+ return (0, guards_1.isText)(name) ? name : undefined;
67
68
  }
68
69
  function resolveLayer(layerPath, mod) {
69
70
  return {
@@ -4,6 +4,7 @@ exports.InvalidLocaleFileError = void 0;
4
4
  exports.readLocaleFile = readLocaleFile;
5
5
  const node_fs_1 = require("node:fs");
6
6
  const message_compiler_1 = require("@intlify/message-compiler");
7
+ const guards_1 = require("./internal/guards");
7
8
  class InvalidLocaleFileError extends Error {
8
9
  constructor(path, reason) {
9
10
  super(`Invalid locale file ${path}: ${reason}`);
@@ -19,10 +20,14 @@ const LITERAL_ESCAPES = {
19
20
  "}": "{'}'}",
20
21
  "|": "{'|'}",
21
22
  };
23
+ function isReservedCharacter(character) {
24
+ return Object.prototype.hasOwnProperty.call(LITERAL_ESCAPES, character);
25
+ }
22
26
  function reservedCharacterAt(message, offset) {
23
27
  for (let index = Math.min(offset, message.length - 1); index >= 0; index--) {
24
- if (message[index] in LITERAL_ESCAPES)
25
- return message[index];
28
+ const character = message[index];
29
+ if (isReservedCharacter(character))
30
+ return character;
26
31
  }
27
32
  return null;
28
33
  }
@@ -46,11 +51,11 @@ function firstCompileError(message) {
46
51
  return first;
47
52
  }
48
53
  function messageProblems(value, key) {
49
- if (typeof value === "string") {
54
+ if ((0, guards_1.isText)(value)) {
50
55
  const error = firstCompileError(value);
51
56
  return error ? [{ key, message: value, error }] : [];
52
57
  }
53
- if (typeof value !== "object" || value === null)
58
+ if (!(0, guards_1.isObjectLike)(value))
54
59
  return [];
55
60
  return Object.entries(value).flatMap(([child, nested]) => messageProblems(nested, key ? `${key}.${child}` : child));
56
61
  }
@@ -7,11 +7,15 @@ exports.formatSecretConflicts = formatSecretConflicts;
7
7
  exports.describeSecretSources = describeSecretSources;
8
8
  exports.reportManifestSecrets = reportManifestSecrets;
9
9
  const cli_1 = require("@antelopejs/core/cli");
10
+ const guards_1 = require("./internal/guards");
10
11
  exports.MANIFEST_SECRET_ENV = {
11
12
  DMS_HTML_RENDER_SECRET: ["htmlRender", "serviceSecret"],
12
13
  DMS_OAUTH_RELAY_SECRET: ["oauth", "relaySecret"],
13
14
  };
14
- const SECRET_NAMES = Object.keys(exports.MANIFEST_SECRET_ENV);
15
+ function isSecretName(name) {
16
+ return Object.prototype.hasOwnProperty.call(exports.MANIFEST_SECRET_ENV, name);
17
+ }
18
+ const SECRET_NAMES = Object.keys(exports.MANIFEST_SECRET_ENV).filter(isSecretName);
15
19
  const UNSET_CONSEQUENCE = {
16
20
  DMS_HTML_RENDER_SECRET: "e-mail renders will be refused",
17
21
  DMS_OAUTH_RELAY_SECRET: "OAuth sign-in will be refused by the backend",
@@ -19,7 +23,7 @@ const UNSET_CONSEQUENCE = {
19
23
  function readPath(options, path) {
20
24
  let value = options;
21
25
  for (const key of path) {
22
- if (!value || typeof value !== "object" || Array.isArray(value))
26
+ if (!(0, guards_1.isObjectLike)(value) || Array.isArray(value))
23
27
  return undefined;
24
28
  value = value[key];
25
29
  }
@@ -36,7 +40,7 @@ function collectManifestSecrets(modules) {
36
40
  const ignored = [];
37
41
  for (const module of ordered) {
38
42
  const value = readPath(module.privateOptions, exports.MANIFEST_SECRET_ENV[name]);
39
- if (typeof value !== "string" || value === "")
43
+ if (!(0, guards_1.isText)(value) || value === "")
40
44
  continue;
41
45
  const first = published[name];
42
46
  if (!first)
@@ -53,7 +57,10 @@ function collectManifestSecrets(modules) {
53
57
  function resolveManifestSecrets(secrets, env = process.env) {
54
58
  const resolved = {
55
59
  env: {},
56
- sources: {},
60
+ sources: {
61
+ DMS_HTML_RENDER_SECRET: "not set",
62
+ DMS_OAUTH_RELAY_SECRET: "not set",
63
+ },
57
64
  conflicts: [],
58
65
  };
59
66
  for (const name of SECRET_NAMES) {
package/dist/manifest.js CHANGED
@@ -16,6 +16,7 @@ const cli_1 = require("@antelopejs/core/cli");
16
16
  const config_1 = require("./config");
17
17
  const output_1 = require("./output");
18
18
  const workspace_1 = require("./workspace");
19
+ const guards_1 = require("./internal/guards");
19
20
  class ManifestUnauthorizedError extends cli_1.CliError {
20
21
  constructor(url, status, credentialSent) {
21
22
  super({
@@ -44,7 +45,7 @@ const UNREACHABLE_REASONS = {
44
45
  const UNKNOWN_HOST_CODES = ["ENOTFOUND", "EAI_AGAIN"];
45
46
  class BackendUnreachableError extends cli_1.CliError {
46
47
  constructor(url, failure) {
47
- const cause = failure?.cause ?? failure;
48
+ const cause = (hasProperties(failure) ? failure.cause : undefined) ?? failure;
48
49
  const code = errorCode(cause);
49
50
  const detail = code
50
51
  ? (UNREACHABLE_REASONS[code] ?? errorMessage(cause))
@@ -63,9 +64,14 @@ class BackendUnreachableError extends cli_1.CliError {
63
64
  }
64
65
  }
65
66
  exports.BackendUnreachableError = BackendUnreachableError;
67
+ function hasProperties(value) {
68
+ return (value !== null && (typeof value === "object" || typeof value === "function"));
69
+ }
66
70
  function errorCode(err) {
67
- const { code, errors } = (err ?? {});
68
- if (typeof code === "string")
71
+ if (!hasProperties(err))
72
+ return undefined;
73
+ const { code, errors } = err;
74
+ if ((0, guards_1.isText)(code))
69
75
  return code;
70
76
  return Array.isArray(errors) ? errorCode(errors[0]) : undefined;
71
77
  }
@@ -101,7 +107,7 @@ async function fetchFromBackend(backendUrl, url, bootstrapSecret) {
101
107
  }
102
108
  class ManifestRefusedError extends cli_1.CliError {
103
109
  constructor(problem) {
104
- super(typeof problem === "string" ? { title: problem } : problem);
110
+ super((0, guards_1.isText)(problem) ? { title: problem } : problem);
105
111
  this.name = "ManifestRefusedError";
106
112
  }
107
113
  }
@@ -112,7 +118,7 @@ const LOADER_UPDATE_COMMAND = "ajs update dms";
112
118
  function describeManifestVersion(version) {
113
119
  const loader = `${OWN_RELEASE.name} ${OWN_RELEASE.version}`;
114
120
  const supported = `This loader reads version ${workspace_1.FRONTEND_MANIFEST_VERSION}`;
115
- if (typeof version !== "number") {
121
+ if (!(0, guards_1.isNumber)(version)) {
116
122
  return {
117
123
  title: "The backend served a frontend manifest without a protocol version",
118
124
  reason: `${supported}; the answer does not look like one from ${BACKEND_PACKAGE}.`,