@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
@@ -15,6 +15,7 @@ const derived_outputs_1 = require("./derived-outputs");
15
15
  const layers_1 = require("./layers");
16
16
  const manifest_secrets_1 = require("./manifest-secrets");
17
17
  const fs_sync_1 = require("./fs-sync");
18
+ const guards_1 = require("./internal/guards");
18
19
  function getPackageRoot() {
19
20
  return (0, node_path_1.resolve)(__dirname, "..");
20
21
  }
@@ -87,7 +88,7 @@ function collectAuthEstablishEndpoints(layers) {
87
88
  const endpoints = new Set();
88
89
  for (const layer of layers) {
89
90
  for (const endpoint of layer.authEstablishEndpoints ?? []) {
90
- if (typeof endpoint === "string" && BACKEND_API_PATH.test(endpoint)) {
91
+ if ((0, guards_1.isText)(endpoint) && BACKEND_API_PATH.test(endpoint)) {
91
92
  endpoints.add(endpoint);
92
93
  continue;
93
94
  }
@@ -103,11 +104,11 @@ function writeAuthEstablishEndpoints(workspaceDir, layers) {
103
104
  (0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspaceDir, config_1.AUTH_ESTABLISH_FILE), `${JSON.stringify({ endpoints }, null, 2)}\n`);
104
105
  }
105
106
  function collectPublicEndpoints(layers) {
106
- return layers.flatMap((layer) => (Array.isArray(layer.publicEndpoints) ? layer.publicEndpoints : []).filter((endpoint) => typeof endpoint === "object" && endpoint !== null));
107
+ return layers.flatMap((layer) => (Array.isArray(layer.publicEndpoints) ? layer.publicEndpoints : []).filter(guards_1.isObjectLike));
107
108
  }
108
109
  function collectBackendProxies(layers) {
109
110
  return layers.flatMap((layer) => (Array.isArray(layer.backendProxies) ? layer.backendProxies : [])
110
- .filter((proxy) => typeof proxy === "object" && proxy !== null)
111
+ .filter(guards_1.isObjectLike)
111
112
  .map((proxy) => ({
112
113
  ...proxy,
113
114
  module: layer.name ?? layer.packageName ?? "unknown",
@@ -121,9 +122,9 @@ function writePublicEndpoints(workspaceDir, layers) {
121
122
  (0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspaceDir, config_1.PUBLIC_ENDPOINTS_FILE), `${JSON.stringify(declared, null, 2)}\n`);
122
123
  }
123
124
  function holdsValue(value) {
124
- if (typeof value === "string")
125
+ if ((0, guards_1.isText)(value))
125
126
  return value !== "";
126
- if (value !== null && typeof value === "object")
127
+ if ((0, guards_1.isObjectLike)(value))
127
128
  return Object.values(value).some(holdsValue);
128
129
  return false;
129
130
  }
@@ -48,8 +48,7 @@ function followModuleSet(options) {
48
48
  inspect(moduleSetFingerprint(manifest.modules));
49
49
  }
50
50
  }
51
- catch {
52
- }
51
+ catch { }
53
52
  if (!isStopped)
54
53
  schedule();
55
54
  };
@@ -102,8 +101,7 @@ async function reportFrontendReadyOnceAnswered(backendUrl, modules, bootstrapSec
102
101
  if (response.status < SERVER_ERROR)
103
102
  return response.ok;
104
103
  }
105
- catch {
106
- }
104
+ catch { }
107
105
  await (0, promises_1.setTimeout)(delayMs, undefined, { ref: false });
108
106
  delayMs = Math.min(retry.maxDelayMs, delayMs * 2);
109
107
  }
package/dist/ports.js CHANGED
@@ -5,6 +5,7 @@ exports.reserveFreePort = reserveFreePort;
5
5
  exports.reservePort = reservePort;
6
6
  const node_net_1 = require("node:net");
7
7
  const cli_1 = require("@antelopejs/core/cli");
8
+ const guards_1 = require("./internal/guards");
8
9
  exports.PORT_FALLBACK_RANGE = 20;
9
10
  exports.MAX_TCP_PORT = 65535;
10
11
  function tryListen(port) {
@@ -27,12 +28,18 @@ async function reserveFreePort(preferred, range = exports.PORT_FALLBACK_RANGE) {
27
28
  fixes: ["Free one of these ports, or pass another one: -p <port>"],
28
29
  });
29
30
  }
31
+ function listeningPort(server) {
32
+ const address = server.address();
33
+ if (!(0, guards_1.isObjectLike)(address))
34
+ throw new Error("The reserved server does not listen on a TCP port");
35
+ return address.port;
36
+ }
30
37
  async function reservePort(port) {
31
38
  const server = await tryListen(port);
32
39
  if (!server)
33
40
  return undefined;
34
41
  return {
35
- port: server.address().port,
42
+ port: listeningPort(server),
36
43
  release: () => new Promise((resolve) => {
37
44
  server.close(() => resolve());
38
45
  }),
@@ -6,26 +6,33 @@ exports.filledOptions = filledOptions;
6
6
  exports.parsePrivateOptions = parsePrivateOptions;
7
7
  exports.unfilledPrivateValues = unfilledPrivateValues;
8
8
  const config_1 = require("./config");
9
+ const guards_1 = require("./internal/guards");
10
+ function isOptions(value) {
11
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12
+ }
13
+ function isContainer(value) {
14
+ return typeof value === "object" && value !== null;
15
+ }
9
16
  function emptied(value) {
10
- if (typeof value === "string")
17
+ if ((0, guards_1.isText)(value))
11
18
  return "";
12
19
  if (Array.isArray(value))
13
20
  return value.map(emptied);
14
- if (value !== null && typeof value === "object")
21
+ if (isOptions(value))
15
22
  return emptiedOptions(value);
16
23
  return value;
17
24
  }
18
25
  function emptiedOptions(options) {
19
26
  return Object.fromEntries(Object.entries(options).map(([key, value]) => [key, emptied(value)]));
20
27
  }
21
- function filled(shape, value) {
22
- if (typeof shape === "string")
23
- return typeof value === "string" ? value : "";
24
- if (Array.isArray(shape))
25
- return shape.map((item, index) => filled(item, Array.isArray(value) ? value[index] : undefined));
26
- if (shape !== null && typeof shape === "object")
27
- return filledOptions(shape, isOptions(value) ? value : undefined);
28
- return shape;
28
+ function filled(declared, value) {
29
+ if ((0, guards_1.isText)(declared))
30
+ return (0, guards_1.isText)(value) ? value : "";
31
+ if (Array.isArray(declared))
32
+ return declared.map((item, index) => filled(item, Array.isArray(value) ? value[index] : undefined));
33
+ if (isOptions(declared))
34
+ return filledOptions(declared, isOptions(value) ? value : undefined);
35
+ return declared;
29
36
  }
30
37
  function filledOptions(shape, values) {
31
38
  return Object.fromEntries(Object.entries(shape).map(([key, value]) => [
@@ -34,8 +41,10 @@ function filledOptions(shape, values) {
34
41
  ]));
35
42
  }
36
43
  exports.PRIVATE_OPTIONS_VARIABLE = "DMS_PRIVATE_OPTIONS";
37
- function isOptions(value) {
38
- return typeof value === "object" && value !== null && !Array.isArray(value);
44
+ function isOptionsById(value) {
45
+ return ((0, guards_1.isObjectLike)(value) &&
46
+ !Array.isArray(value) &&
47
+ Object.values(value).every(isOptions));
39
48
  }
40
49
  function parsePrivateOptions(raw) {
41
50
  if (!raw?.trim())
@@ -47,7 +56,7 @@ function parsePrivateOptions(raw) {
47
56
  catch {
48
57
  given = undefined;
49
58
  }
50
- if (!isOptions(given) || !Object.values(given).every(isOptions)) {
59
+ if (!isOptionsById(given)) {
51
60
  throw new config_1.UsageError({
52
61
  title: `${exports.PRIVATE_OPTIONS_VARIABLE} is not a JSON object of options by module id`,
53
62
  fixes: [
@@ -60,18 +69,18 @@ function parsePrivateOptions(raw) {
60
69
  function readPath(options, path) {
61
70
  let value = options;
62
71
  for (const key of path) {
63
- if (typeof value !== "object" || value === null)
72
+ if (!isContainer(value))
64
73
  return undefined;
65
74
  if (!Object.prototype.hasOwnProperty.call(value, key))
66
75
  return undefined;
67
- value = value[key];
76
+ value = isOptions(value) ? value[key] : value[Number(key)];
68
77
  }
69
78
  return value;
70
79
  }
71
80
  function* emptyStrings(value, path = []) {
72
81
  if (value === "")
73
82
  yield path;
74
- else if (typeof value === "object" && value !== null)
83
+ else if (isContainer(value))
75
84
  for (const [key, child] of Object.entries(value))
76
85
  yield* emptyStrings(child, [...path, key]);
77
86
  }
@@ -82,7 +91,7 @@ function unfilledPrivateValues(modules, given, covered) {
82
91
  .filter((path) => path.length > 0 && !isCovered(path))
83
92
  .filter((path) => {
84
93
  const value = readPath(given[module], path);
85
- return typeof value !== "string" || value === "";
94
+ return !(0, guards_1.isText)(value) || value === "";
86
95
  })
87
96
  .map((path) => ({ module, path })));
88
97
  }
@@ -41,6 +41,7 @@ const node_fs_1 = require("node:fs");
41
41
  const fsPromises = __importStar(require("node:fs/promises"));
42
42
  const node_path_1 = require("node:path");
43
43
  const cli_1 = require("@antelopejs/core/cli");
44
+ const guards_1 = require("./internal/guards");
44
45
  const layers_1 = require("./layers");
45
46
  const private_values_1 = require("./private-values");
46
47
  exports.FRONTEND_DECLARATION_FIELD = "dmsFrontend";
@@ -66,9 +67,12 @@ const NODE_FILE_SYSTEM = {
66
67
  };
67
68
  function readAntelopeManifest(folder) {
68
69
  const dedicated = (0, node_path_1.join)(folder, MODULE_MANIFEST_FILE);
69
- if ((0, node_fs_1.existsSync)(dedicated))
70
- return JSON.parse((0, node_fs_1.readFileSync)(dedicated, "utf-8"));
71
- return (0, layers_1.readLayerPackage)(folder)?.antelopeJs;
70
+ if ((0, node_fs_1.existsSync)(dedicated)) {
71
+ const manifest = JSON.parse((0, node_fs_1.readFileSync)(dedicated, "utf-8"));
72
+ return (0, guards_1.isObjectLike)(manifest) ? manifest : undefined;
73
+ }
74
+ const block = (0, layers_1.readLayerPackage)(folder)?.antelopeJs;
75
+ return (0, guards_1.isObjectLike)(block) ? block : undefined;
72
76
  }
73
77
  function warnUndeclaredConfigVars(id) {
74
78
  (0, cli_1.getProcessUi)().message("warn", `${id} declares no antelopeJs.configVarsFile in its package.json: the \${@${id}.<VAR>} references are left unresolved`, { detail: `Upgrade ${id} to a version that declares it` });
@@ -76,19 +80,19 @@ function warnUndeclaredConfigVars(id) {
76
80
  function configVarsProvider(id, folder) {
77
81
  const manifest = readAntelopeManifest(folder);
78
82
  const file = manifest?.configVarsFile;
79
- if (typeof file !== "string") {
83
+ if (!(0, guards_1.isText)(file)) {
80
84
  warnUndeclaredConfigVars(id);
81
85
  return undefined;
82
86
  }
83
87
  const { configVars } = require((0, node_path_1.join)(folder, file));
84
- if (typeof configVars !== "function") {
88
+ if (!(0, guards_1.isCallable)(configVars)) {
85
89
  throw new cli_1.CliError({
86
90
  title: `${id}: ${file} exports no configVars function`,
87
91
  fixes: [`Export configVars(config) from ${file}`],
88
92
  });
89
93
  }
90
94
  const declared = Array.isArray(manifest?.configVars)
91
- ? manifest.configVars.filter((name) => typeof name === "string")
95
+ ? manifest.configVars.filter(guards_1.isText)
92
96
  : [];
93
97
  return { declared, configVars };
94
98
  }
@@ -146,7 +150,7 @@ async function loadProjectConfig(projectDir, environment) {
146
150
  const loader = new ConfigLoader(NODE_FILE_SYSTEM);
147
151
  let loaded;
148
152
  try {
149
- loaded = (await loader.load(projectDir, environment));
153
+ loaded = await loader.load(projectDir, environment);
150
154
  }
151
155
  catch (error) {
152
156
  throw new cli_1.CliError({
@@ -185,7 +189,9 @@ function moduleFolder(projectDir, cacheFolder, source) {
185
189
  function readShippingVersion(sourcePath) {
186
190
  try {
187
191
  const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)((0, node_path_1.dirname)(sourcePath), "package.json"), "utf-8"));
188
- return typeof pkg.version === "string" ? pkg.version : undefined;
192
+ return (0, guards_1.isObjectLike)(pkg) && "version" in pkg && (0, guards_1.isText)(pkg.version)
193
+ ? pkg.version
194
+ : undefined;
189
195
  }
190
196
  catch {
191
197
  return undefined;
@@ -197,7 +203,7 @@ function toLayer(registration) {
197
203
  path: registration.sourcePath,
198
204
  name: registration.name,
199
205
  sourcePath: registration.sourcePath,
200
- packageName: typeof name === "string" ? name : undefined,
206
+ packageName: (0, guards_1.isText)(name) ? name : undefined,
201
207
  priority: registration.priority ?? 0,
202
208
  configKey: registration.configKey,
203
209
  options: registration.options ?? {},
@@ -218,12 +224,12 @@ function warnUndeclaredFrontend(id, folder) {
218
224
  }
219
225
  function registrationOf(id, folder, config, context) {
220
226
  const declaration = (0, layers_1.readLayerPackage)(folder)?.[exports.FRONTEND_DECLARATION_FIELD];
221
- if (typeof declaration !== "string") {
227
+ if (!(0, guards_1.isText)(declaration)) {
222
228
  warnUndeclaredFrontend(id, folder);
223
229
  return undefined;
224
230
  }
225
231
  const { frontendModule } = require((0, node_path_1.join)(folder, declaration));
226
- if (typeof frontendModule !== "function") {
232
+ if (!(0, guards_1.isCallable)(frontendModule)) {
227
233
  throw new cli_1.CliError({
228
234
  title: `${id}: ${declaration} exports no frontendModule function`,
229
235
  fixes: [`Export frontendModule(config, context) from ${declaration}`],
@@ -10,6 +10,7 @@ const cli_1 = require("@antelopejs/core/cli");
10
10
  const semver_1 = __importDefault(require("semver"));
11
11
  const layers_1 = require("./layers");
12
12
  const output_1 = require("./output");
13
+ const guards_1 = require("./internal/guards");
13
14
  const OWN_RELEASE = require("../package.json");
14
15
  const noticedModules = new Set();
15
16
  const NOTICED_MODULES_FILE = ".renderer-range-noticed";
@@ -45,7 +46,7 @@ function releaseLine(version) {
45
46
  }
46
47
  function describeProblem(declaration, version) {
47
48
  const { shown, range } = declaration;
48
- if (typeof range !== "string" || semver_1.default.validRange(range) === null)
49
+ if (!(0, guards_1.isText)(range) || semver_1.default.validRange(range) === null)
49
50
  return `${shown} declares an unreadable range: ${JSON.stringify(range)}`;
50
51
  if (semver_1.default.satisfies(releaseLine(version), range))
51
52
  return undefined;
@@ -11,6 +11,7 @@ const node_path_1 = require("node:path");
11
11
  const cli_1 = require("@antelopejs/core/cli");
12
12
  const cancellation_1 = require("./cancellation");
13
13
  const workspace_setup_1 = require("./workspace-setup");
14
+ const guards_1 = require("./internal/guards");
14
15
  const READY_MESSAGE = "dms:ready";
15
16
  const LISTEN_ERROR_MESSAGE = "dms:listen-error";
16
17
  const START_ERROR_MESSAGE = "dms:start-error";
@@ -36,8 +37,7 @@ function stopOnAbort(child, signal) {
36
37
  process.kill(-child.pid, "SIGTERM");
37
38
  }
38
39
  }
39
- catch {
40
- }
40
+ catch { }
41
41
  };
42
42
  if (signal.aborted) {
43
43
  stop();
@@ -46,43 +46,44 @@ function stopOnAbort(child, signal) {
46
46
  signal.addEventListener("abort", stop, { once: true });
47
47
  child.once("exit", () => signal.removeEventListener("abort", stop));
48
48
  }
49
- function isRecord(value) {
50
- return typeof value === "object" && value !== null;
49
+ function hasFile(value) {
50
+ return (0, guards_1.isObjectLike)(value) && "file" in value && (0, guards_1.isText)(value.file);
51
+ }
52
+ function isServerMessage(message) {
53
+ return (0, guards_1.isObjectLike)(message);
51
54
  }
52
55
  function readReady(message) {
53
- if (!isRecord(message) || message.type !== READY_MESSAGE)
56
+ if (!isServerMessage(message) || message.type !== READY_MESSAGE)
54
57
  return undefined;
55
58
  const { address, port } = message;
56
- if (typeof address !== "string" || typeof port !== "number")
59
+ if (!(0, guards_1.isText)(address) || !(0, guards_1.isNumber)(port))
57
60
  return undefined;
58
61
  return { address, port };
59
62
  }
60
63
  function readListenError(message) {
61
- if (!isRecord(message) || message.type !== LISTEN_ERROR_MESSAGE)
64
+ if (!isServerMessage(message) || message.type !== LISTEN_ERROR_MESSAGE)
62
65
  return undefined;
63
66
  return {
64
- code: typeof message.code === "string" ? message.code : undefined,
67
+ code: (0, guards_1.isText)(message.code) ? message.code : undefined,
65
68
  message: String(message.message),
66
69
  host: String(message.host),
67
70
  port: Number(message.port),
68
71
  };
69
72
  }
70
73
  function readStartError(message) {
71
- if (!isRecord(message) || message.type !== START_ERROR_MESSAGE)
74
+ if (!isServerMessage(message) || message.type !== START_ERROR_MESSAGE)
72
75
  return undefined;
73
76
  const { location } = message;
74
- const isLocated = isRecord(location) && typeof location.file === "string";
77
+ const isLocated = hasFile(location);
75
78
  return {
76
79
  message: String(message.message),
77
- code: typeof message.code === "string" ? message.code : undefined,
80
+ code: (0, guards_1.isText)(message.code) ? message.code : undefined,
78
81
  location: isLocated
79
82
  ? {
80
83
  file: String(location.file),
81
- line: typeof location.line === "number" ? location.line : undefined,
82
- column: typeof location.column === "number" ? location.column : undefined,
83
- lineText: typeof location.lineText === "string"
84
- ? location.lineText
85
- : undefined,
84
+ line: (0, guards_1.isNumber)(location.line) ? location.line : undefined,
85
+ column: (0, guards_1.isNumber)(location.column) ? location.column : undefined,
86
+ lineText: (0, guards_1.isText)(location.lineText) ? location.lineText : undefined,
86
87
  }
87
88
  : undefined,
88
89
  };
@@ -48,6 +48,7 @@ const node_path_1 = require("node:path");
48
48
  const cli_1 = require("@antelopejs/core/cli");
49
49
  const config_1 = require("./config");
50
50
  const output_1 = require("./output");
51
+ const guards_1 = require("./internal/guards");
51
52
  exports.UPDATE_CHECK_PACKAGE = "@antelopejs/dms-frontend";
52
53
  const DEFAULT_REGISTRY_URL = "https://registry.npmjs.org/";
53
54
  const UPDATE_COMMAND = "ajs update dms";
@@ -104,20 +105,24 @@ function isUpdateCheckEnabled(argv, env) {
104
105
  function updateCheckCacheFile() {
105
106
  return (0, node_path_1.join)(config_1.DMS_FRONTEND_HOME, "update-check.json");
106
107
  }
108
+ function isUpdateCheckCache(value) {
109
+ return ((0, guards_1.isObjectLike)(value) &&
110
+ "checkedAt" in value &&
111
+ (0, guards_1.isNumber)(value.checkedAt) &&
112
+ (!("latestVersion" in value) ||
113
+ value.latestVersion === undefined ||
114
+ (0, guards_1.isText)(value.latestVersion)) &&
115
+ (!("succeeded" in value) ||
116
+ value.succeeded === undefined ||
117
+ (0, guards_1.isFlag)(value.succeeded)));
118
+ }
107
119
  function readCache(file) {
108
120
  try {
109
121
  const parsed = JSON.parse((0, node_fs_1.readFileSync)(file, "utf-8"));
110
- if (typeof parsed?.checkedAt !== "number")
111
- return undefined;
112
- const latestVersion = parsed.latestVersion;
113
- if (latestVersion !== undefined && typeof latestVersion !== "string") {
114
- return undefined;
115
- }
116
- const succeeded = parsed.succeeded;
117
- if (succeeded !== undefined && typeof succeeded !== "boolean") {
122
+ if (!isUpdateCheckCache(parsed))
118
123
  return undefined;
119
- }
120
- return { checkedAt: parsed.checkedAt, latestVersion, succeeded };
124
+ const { checkedAt, latestVersion, succeeded } = parsed;
125
+ return { checkedAt, latestVersion, succeeded };
121
126
  }
122
127
  catch {
123
128
  return undefined;
@@ -128,8 +133,7 @@ function writeCache(file, cache) {
128
133
  (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(file), { recursive: true });
129
134
  (0, node_fs_1.writeFileSync)(file, `${JSON.stringify(cache)}\n`);
130
135
  }
131
- catch {
132
- }
136
+ catch { }
133
137
  }
134
138
  function fetchLatestVersionFromRegistry(packageName, registryUrl = DEFAULT_REGISTRY_URL, timeoutMs = exports.UPDATE_CHECK_TIMEOUT_MS) {
135
139
  return new Promise((resolve) => {
@@ -160,8 +164,12 @@ function fetchLatestVersionFromRegistry(packageName, registryUrl = DEFAULT_REGIS
160
164
  });
161
165
  response.on("end", () => {
162
166
  try {
163
- const version = JSON.parse(body)?.version;
164
- settle(typeof version === "string" ? version : undefined);
167
+ const parsed = JSON.parse(body);
168
+ settle((0, guards_1.isObjectLike)(parsed) &&
169
+ "version" in parsed &&
170
+ (0, guards_1.isText)(parsed.version)
171
+ ? parsed.version
172
+ : undefined);
165
173
  }
166
174
  catch {
167
175
  settle();
@@ -220,8 +228,7 @@ function startUpdateCheck(options) {
220
228
  latestVersion,
221
229
  succeeded: published !== undefined,
222
230
  });
223
- }, () => {
224
- });
231
+ }, () => { });
225
232
  let isReported = false;
226
233
  const report = async (placement = {}) => {
227
234
  if (isReported)
@@ -98,7 +98,7 @@ function checkClientBundle(workspace) {
98
98
  const clientRoot = (0, node_path_1.join)(workspace, "dist", "client");
99
99
  const manifest = readJson((0, node_path_1.join)(clientRoot, ".vite", "manifest.json"));
100
100
  const mainEntry = Object.values(manifest).find((entry) => entry.isEntry);
101
- ensure(mainEntry, "The Vite manifest names no entry chunk");
101
+ ensure(mainEntry !== undefined, "The Vite manifest names no entry chunk");
102
102
  const mainJavascript = (0, node_fs_1.readFileSync)((0, node_path_1.join)(clientRoot, mainEntry.file), "utf8");
103
103
  const javascript = (0, node_fs_1.readdirSync)((0, node_path_1.join)(clientRoot, "assets"))
104
104
  .filter((file) => file.endsWith(".js"))
@@ -119,7 +119,7 @@ async function checkServerModulesBundle(workspace) {
119
119
  .map((file) => (0, node_fs_1.readFileSync)((0, node_path_1.join)(root, file), "utf8"))
120
120
  .join("\n");
121
121
  ensure(!/\.vue["'?]|createApp\(|DefaultLayout/.test(javascript), "The server modules bundle loads application code", "A dms.server.ts imports server code only, never .vue files or dms.frontend.ts.");
122
- const { serverModules } = (await Promise.resolve(`${(0, node_path_1.join)(root, "server-modules.generated.js")}`).then(s => __importStar(require(s))));
122
+ const { serverModules } = await Promise.resolve(`${(0, node_path_1.join)(root, "server-modules.generated.js")}`).then(s => __importStar(require(s)));
123
123
  ensure(Array.isArray(serverModules), "The server modules bundle exports none");
124
124
  return `${(0, cli_1.pluralize)(serverModules.length, "server module")}, ${(0, output_1.formatSize)(Buffer.byteLength(javascript))} JS`;
125
125
  }
@@ -127,8 +127,7 @@ function runCommand(command, args, options, onSpawn) {
127
127
  process.kill(-child.pid, signal);
128
128
  }
129
129
  }
130
- catch {
131
- }
130
+ catch { }
132
131
  };
133
132
  const stopChild = (signal) => {
134
133
  if (!alive())
package/dist/workspace.js CHANGED
@@ -15,6 +15,7 @@ exports.isUnauthorized = isUnauthorized;
15
15
  const node_fs_1 = require("node:fs");
16
16
  const node_path_1 = require("node:path");
17
17
  const config_1 = require("./config");
18
+ const guards_1 = require("./internal/guards");
18
19
  const WORKSPACE_META_FILE = ".ajs-dms-meta.json";
19
20
  function getWorkspaceDirForKey(key) {
20
21
  return (0, node_path_1.join)(config_1.DMS_FRONTEND_HOME, (0, config_1.sha256Hex)(key));
@@ -75,15 +76,16 @@ function readWorkspaceMeta(dir) {
75
76
  catch {
76
77
  return undefined;
77
78
  }
78
- const record = meta;
79
+ const backendUrl = (0, guards_1.isObjectLike)(meta) && "backendUrl" in meta ? meta.backendUrl : undefined;
80
+ const workspaceKey = (0, guards_1.isObjectLike)(meta) && "workspaceKey" in meta
81
+ ? meta.workspaceKey
82
+ : undefined;
83
+ const updatedAt = (0, guards_1.isObjectLike)(meta) && "updatedAt" in meta ? meta.updatedAt : undefined;
79
84
  return {
80
- backendUrl: typeof record?.backendUrl === "string" ? record.backendUrl : undefined,
81
- workspaceKey: typeof record?.workspaceKey === "string"
82
- ? record.workspaceKey
83
- : undefined,
84
- updatedAt: typeof record?.updatedAt === "string" &&
85
- !Number.isNaN(Date.parse(record.updatedAt))
86
- ? new Date(record.updatedAt).toISOString()
85
+ backendUrl: (0, guards_1.isText)(backendUrl) ? backendUrl : undefined,
86
+ workspaceKey: (0, guards_1.isText)(workspaceKey) ? workspaceKey : undefined,
87
+ updatedAt: (0, guards_1.isText)(updatedAt) && !Number.isNaN(Date.parse(updatedAt))
88
+ ? new Date(updatedAt).toISOString()
87
89
  : undefined,
88
90
  };
89
91
  }
@@ -116,8 +118,7 @@ function directorySize(dir) {
116
118
  seen.add(inode);
117
119
  total += stats.size;
118
120
  }
119
- catch {
120
- }
121
+ catch { }
121
122
  }
122
123
  }
123
124
  return total;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antelopejs/dms-frontend",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "Frontend-agnostic loader for AntelopeJS DMS, shipping the Vue 3 renderer (Vite, Inertia, SSR)",
5
5
  "keywords": [
6
6
  "antelope",
@@ -65,13 +65,14 @@
65
65
  "test:cli": "pnpm run build && DMS_PREBUILT=1 node --import tsx --test tests/cli.test.ts 'tests/cli-*.test.{ts,mts}'",
66
66
  "test:real-source": "pnpm run build && node dist/verify-source-runner.js",
67
67
  "format": "oxfmt .",
68
- "lint": "oxlint && antelopejs-check-interface-ranges && pnpm run docs:reference:check",
68
+ "lint": "oxlint && antelopejs-check-interface-ranges && pnpm run docs:reference:check && pnpm run source-checks",
69
69
  "typecheck": "tsc --noEmit",
70
70
  "lint:fix": "oxlint --fix --max-warnings=1000000 && oxfmt .",
71
71
  "knip": "knip --dependencies",
72
72
  "knip:all": "knip",
73
73
  "docs:reference": "tsx scripts/docs-reference.ts",
74
74
  "docs:reference:check": "tsx scripts/docs-reference.ts --check",
75
+ "source-checks": "tsx scripts/source-checks.ts",
75
76
  "prepare": "if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi",
76
77
  "checks": "antelopejs-checks",
77
78
  "size": "antelopejs-check-size",
@@ -79,7 +80,9 @@
79
80
  "sync": "antelopejs-sync",
80
81
  "ratchet:update": "antelopejs-ratchet --update",
81
82
  "sync:check": "antelopejs-sync --check",
82
- "format:check": "oxfmt --check ."
83
+ "format:check": "oxfmt --check .",
84
+ "suppressions": "antelopejs-suppressions",
85
+ "suppressions:update": "antelopejs-suppressions --update"
83
86
  },
84
87
  "dependencies": {
85
88
  "@intlify/message-compiler": "^11.1.12",
@@ -90,7 +93,7 @@
90
93
  },
91
94
  "devDependencies": {
92
95
  "@antelopejs/core": "^1.16.0",
93
- "@antelopejs/tooling-configs": "^0.2.0",
96
+ "@antelopejs/tooling-configs": "^0.2.17",
94
97
  "@iconify/utils": "^3.1.0",
95
98
  "@iconify/vue": "^5.0.1",
96
99
  "@inertiajs/vue3": "^3.7.1",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: dms-frontend
3
- description: Runs, builds, deploys and extends the AntelopeJS DMS frontend with @antelopejs/dms-frontend (the ajs dms CLI, the Vue renderer, the frontend-module SDK). Use when running ajs dms dev, build, start, prepare, workspaces, clean or verify-source; configuring DMS_* environment variables, the session or bootstrap secret; writing a frontend module's dms.frontend.ts, dms.server.ts (server routes, auth hooks, route rules, backend proxies) or dms.frontend.build.ts; declaring engines["@antelopejs/dms-frontend"]; deploying the frontend server behind a reverse proxy; or debugging a refused range, a missing secret, a 404 from the proxy or a stale workspace.
3
+ description: Runs, builds, deploys and extends the AntelopeJS DMS frontend with @antelopejs/dms-frontend (the ajs dms CLI, the Vue renderer, the frontend-module SDK). Use when running ajs dms dev, build, start, prepare, workspaces, clean or verify-source; configuring DMS_* environment variables, the session or bootstrap secret; writing a frontend module's dms.frontend.ts, dms.server.ts (server routes, auth hooks, route rules, backend proxies) or dms.frontend.build.ts; declaring engines["@antelopejs/dms-frontend"]; deploying the frontend server behind a reverse proxy; or debugging a refused range, a missing secret, a 404 from the proxy or a stale workspace. Not for restarting a DMS project's running backend and frontend dev servers or reading their logs (use dms-project).
4
4
  icon: i-lucide-app-window
5
5
  category: tooling
6
6
  tags: [frontend, cli, renderer, vue, inertia, ssr, deployment, dms-module]
@@ -0,0 +1,77 @@
1
+ {
2
+ "version": 1,
3
+ "cases": [
4
+ {
5
+ "id": "deploy-dashboard",
6
+ "area": "dms",
7
+ "style": "clean",
8
+ "prompt": "Deploy the dashboard to production behind an nginx reverse proxy.",
9
+ "expect": {
10
+ "skills": ["dms-frontend"],
11
+ "acceptable": ["dms-project", "dms"]
12
+ }
13
+ },
14
+ {
15
+ "id": "dms-build-401-casual",
16
+ "area": "dms",
17
+ "style": "casual",
18
+ "prompt": "ajs dms build fails with a 401, wtf",
19
+ "expect": {
20
+ "skills": ["dms-frontend"],
21
+ "acceptable": ["dms-project", "ajs-cli"]
22
+ }
23
+ },
24
+ {
25
+ "id": "frontend-session-secret",
26
+ "area": "dms",
27
+ "style": "clean",
28
+ "prompt": "The frontend server refuses to start because of a missing session secret. How do I configure it?",
29
+ "expect": {
30
+ "skills": ["dms-frontend"],
31
+ "acceptable": ["dms-project"]
32
+ }
33
+ },
34
+ {
35
+ "id": "frontend-server-route",
36
+ "area": "dms",
37
+ "style": "clean",
38
+ "prompt": "Add a server route to my module's frontend (dms.server.ts) that proxies to an external API.",
39
+ "expect": {
40
+ "skills": ["dms-frontend"],
41
+ "acceptable": ["dms-module", "dms"]
42
+ }
43
+ },
44
+ {
45
+ "id": "dms-frontend-not-dev-servers",
46
+ "area": "dms",
47
+ "style": "clean",
48
+ "prompt": "My page change does not show live. How do I restart the backend and the frontend dev servers of my DMS project and read their logs?",
49
+ "expect": {
50
+ "skills": ["dms-project"],
51
+ "acceptable": ["dms"],
52
+ "avoid": ["dms-frontend"]
53
+ }
54
+ },
55
+ {
56
+ "id": "dms-frontend-backend-proxy",
57
+ "area": "dms",
58
+ "style": "clean",
59
+ "prompt": "Browser requests to /reports/summary answer 404 although my backend serves that route. How do I let the frontend server reach it?",
60
+ "expect": {
61
+ "skills": ["dms-frontend"],
62
+ "acceptable": ["dms-project", "dms-module"]
63
+ }
64
+ },
65
+ {
66
+ "id": "dms-frontend-module-entry",
67
+ "area": "dms",
68
+ "style": "clean",
69
+ "prompt": "Write the dms.frontend.ts entry for my module so its components are namespaced under the Acme prefix.",
70
+ "expect": {
71
+ "skills": ["dms-frontend"],
72
+ "acceptable": ["dms-module", "dms"],
73
+ "mustMention": ["componentPrefix", "setup"]
74
+ }
75
+ }
76
+ ]
77
+ }