primate 0.32.7 → 0.33.0

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 (104) hide show
  1. package/lib/app.tsconfig.json +55 -0
  2. package/lib/bin.d.ts +3 -0
  3. package/{src → lib}/bin.js +1 -0
  4. package/lib/commands/build.d.ts +4 -0
  5. package/lib/commands/build.js +9 -0
  6. package/lib/commands/dev.d.ts +3 -0
  7. package/lib/commands/dev.js +8 -0
  8. package/lib/commands/index.d.ts +3 -0
  9. package/lib/commands/index.js +13 -0
  10. package/lib/commands/init.d.ts +2 -0
  11. package/lib/commands/init.js +348 -0
  12. package/lib/commands/serve.d.ts +3 -0
  13. package/lib/commands/serve.js +13 -0
  14. package/lib/commands/test.d.ts +3 -0
  15. package/lib/commands/test.js +133 -0
  16. package/lib/init.d.ts +3 -0
  17. package/lib/init.js +12 -0
  18. package/lib/private/test.d.ts +32 -0
  19. package/lib/private/test.js +8 -0
  20. package/lib/public/Loader.d.ts +2 -0
  21. package/lib/public/Loader.js +2 -0
  22. package/lib/public/Module.d.ts +2 -0
  23. package/lib/public/Module.js +2 -0
  24. package/lib/public/RequestFacade.d.ts +2 -0
  25. package/lib/public/RequestFacade.js +2 -0
  26. package/lib/public/client/app.d.ts +4 -0
  27. package/lib/public/client/app.js +3 -0
  28. package/lib/public/config/i18n.d.ts +2 -0
  29. package/lib/public/config/i18n.js +2 -0
  30. package/lib/public/config/session.d.ts +2 -0
  31. package/lib/public/config/session.js +2 -0
  32. package/lib/public/config.d.ts +2 -0
  33. package/lib/public/config.js +2 -0
  34. package/lib/public/database/default.d.ts +2 -0
  35. package/lib/public/database/default.js +2 -0
  36. package/lib/public/database/wrap.d.ts +2 -0
  37. package/lib/public/database/wrap.js +2 -0
  38. package/lib/public/i18n/locale.d.ts +2 -0
  39. package/lib/public/i18n/locale.js +2 -0
  40. package/lib/public/load-text.d.ts +4 -0
  41. package/lib/public/load-text.js +3 -0
  42. package/lib/public/request/Facade.d.ts +2 -0
  43. package/lib/public/request/Facade.js +2 -0
  44. package/lib/public/response/Status.d.ts +2 -0
  45. package/lib/public/response/Status.js +2 -0
  46. package/lib/public/response/binary.d.ts +2 -0
  47. package/lib/public/response/binary.js +2 -0
  48. package/lib/public/response/error.d.ts +2 -0
  49. package/lib/public/response/error.js +2 -0
  50. package/lib/public/response/json.d.ts +2 -0
  51. package/lib/public/response/json.js +2 -0
  52. package/lib/public/response/redirect.d.ts +2 -0
  53. package/lib/public/response/redirect.js +2 -0
  54. package/lib/public/response/sse.d.ts +2 -0
  55. package/lib/public/response/sse.js +2 -0
  56. package/lib/public/response/text.d.ts +2 -0
  57. package/lib/public/response/text.js +2 -0
  58. package/lib/public/response/view.d.ts +2 -0
  59. package/lib/public/response/view.js +2 -0
  60. package/lib/public/response/ws.d.ts +2 -0
  61. package/lib/public/response/ws.js +2 -0
  62. package/lib/public/response.d.ts +22 -0
  63. package/lib/public/response.js +19 -0
  64. package/lib/public/route.d.ts +3 -0
  65. package/lib/public/route.js +3 -0
  66. package/lib/public/router.d.ts +2 -0
  67. package/lib/public/router.js +2 -0
  68. package/lib/public/s/config.d.ts +2 -0
  69. package/lib/public/s/config.js +2 -0
  70. package/lib/public/s/internal.d.ts +2 -0
  71. package/lib/public/s/internal.js +2 -0
  72. package/lib/public/serve.d.ts +2 -0
  73. package/{src → lib}/public/serve.js +1 -0
  74. package/lib/public/session/Manager.d.ts +2 -0
  75. package/lib/public/session/Manager.js +2 -0
  76. package/lib/public/store.d.ts +4 -0
  77. package/lib/public/store.js +3 -0
  78. package/lib/public/symbol/config.d.ts +2 -0
  79. package/lib/public/symbol/config.js +2 -0
  80. package/lib/public/test.d.ts +2 -0
  81. package/lib/public/test.js +2 -0
  82. package/lib/public/wasm/instantiate.d.ts +2 -0
  83. package/lib/public/wasm/instantiate.js +2 -0
  84. package/lib/runtime/FileRef.d.ts +2 -0
  85. package/lib/runtime/FileRef.js +2 -0
  86. package/package.json +47 -23
  87. package/src/commands/build.js +0 -4
  88. package/src/commands/dev.js +0 -8
  89. package/src/commands/exports.js +0 -5
  90. package/src/commands/serve.js +0 -8
  91. package/src/handlers/error.js +0 -1
  92. package/src/handlers/json.js +0 -1
  93. package/src/handlers/redirect.js +0 -1
  94. package/src/handlers/sse.js +0 -1
  95. package/src/handlers/stream.js +0 -1
  96. package/src/handlers/text.js +0 -1
  97. package/src/handlers/view.js +0 -1
  98. package/src/handlers/ws.js +0 -1
  99. package/src/init.js +0 -12
  100. package/src/public/load-text.js +0 -3
  101. package/src/public/loader.js +0 -30
  102. package/src/public/serve-asset.js +0 -9
  103. package/src/runtime/file.js +0 -3
  104. package/types/index.d.ts +0 -89
@@ -0,0 +1,55 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowJs": true,
4
+ "baseUrl": "${configDir}",
5
+ "target": "esnext",
6
+ "module": "nodenext",
7
+ "moduleResolution": "nodenext",
8
+ "strict": true,
9
+ "skipLibCheck": true,
10
+ "customConditions": ["apekit"],
11
+ "erasableSyntaxOnly": true,
12
+ "exactOptionalPropertyTypes": true,
13
+ "allowImportingTsExtensions": true,
14
+ "paths": {
15
+ "#route/*": ["routes/*"],
16
+ "#component/*": [
17
+ "components/*.tsx",
18
+ "components/*.jsx",
19
+ "components/*.vue",
20
+ "components/*.svelte",
21
+ "components/*.component.ts",
22
+ "components/*.ts",
23
+ "components/*.js",
24
+ "components/*"
25
+ ],
26
+ "#store/*": ["stores/*", "stores/*.ts", "stores/*.js"],
27
+ "#locale/*": ["locales/*", "locales/*.ts", "locales/*.js"],
28
+ "#module/*": ["modules/*", "modules/*.ts", "modules/*.js"],
29
+ "#config/*": ["config/*", "config/*.ts", "config/*.js"],
30
+ "#static/*": ["static/*", "static/*.ts", "static/*.js"],
31
+ "#database/*": ["config/database/*.ts", "config/database/*.js"],
32
+ "#app": ["config/app.ts", "config/app.js"],
33
+ "#session": ["config/session.ts", "config/session.js"],
34
+ "#i18n": ["config/i18n.ts", "config/i18n.js"],
35
+ "#database": [
36
+ "config/database/index.ts",
37
+ "config/database/index.js",
38
+ "config/database/default.js",
39
+ "config/database/default.ts"
40
+ ]
41
+ }
42
+ },
43
+ "include": [
44
+ "${configDir}/config",
45
+ "${configDir}/routes",
46
+ "${configDir}/components",
47
+ "${configDir}/stores",
48
+ "${configDir}/modules",
49
+ "${configDir}/test",
50
+ "${configDir}/static",
51
+ "${configDir}/primate.config.js",
52
+ "${configDir}/primate.config.ts"
53
+ ],
54
+ "exclude": ["node_modules"]
55
+ }
package/lib/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -2,3 +2,4 @@
2
2
  import args from "@rcompat/args";
3
3
  import init from "./init.js";
4
4
  await init(...args);
5
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1,4 @@
1
+ import type Mode from "@primate/core/Mode";
2
+ declare const _default: (flags: string[], mode?: Mode) => Promise<true | undefined>;
3
+ export default _default;
4
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1,9 @@
1
+ import build from "@primate/core/build";
2
+ const T_FLAG = "--target=";
3
+ // build for production
4
+ export default (flags, mode = "production") => {
5
+ const target = flags.find(f => f.startsWith(T_FLAG))?.slice(T_FLAG.length)
6
+ ?? "web";
7
+ return build(mode, target);
8
+ };
9
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1,3 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
3
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1,8 @@
1
+ import build from "./build.js";
2
+ import serve from "./serve.js";
3
+ // build for development and serve
4
+ export default async () => {
5
+ // will only serve is build is successful
6
+ await build(["--target=web"], "development") === true && serve();
7
+ };
8
+ //# sourceMappingURL=dev.js.map
@@ -0,0 +1,3 @@
1
+ declare const _default: (name: string) => ((flags: string[], mode?: import("@primate/core/Mode").default) => Promise<true | undefined>) | (() => Promise<any>);
2
+ export default _default;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,13 @@
1
+ import { default as build } from "./build.js";
2
+ import { default as dev } from "./dev.js";
3
+ import { default as init } from "./init.js";
4
+ import { default as serve } from "./serve.js";
5
+ import { default as test } from "./test.js";
6
+ export default (name) => ({
7
+ build,
8
+ dev,
9
+ init,
10
+ serve,
11
+ test,
12
+ })[name] ?? dev;
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ export default function init(): Promise<symbol>;
2
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1,348 @@
1
+ import cancel from "@rcompat/cli/prompts/cancel";
2
+ import intro from "@rcompat/cli/prompts/intro";
3
+ import isCancel from "@rcompat/cli/prompts/is-cancel";
4
+ import multiselect from "@rcompat/cli/prompts/multiselect";
5
+ import outro from "@rcompat/cli/prompts/outro";
6
+ import select from "@rcompat/cli/prompts/select";
7
+ import text from "@rcompat/cli/prompts/text";
8
+ import FileRef from "@rcompat/fs/FileRef";
9
+ import dedent from "@rcompat/string/dedent";
10
+ const FRONTEND_OPTIONS = [
11
+ { label: "Angular", value: "angular" },
12
+ { label: "Eta", value: "eta" },
13
+ { label: "HTML", value: "html" },
14
+ { label: "HTMX", value: "htmx" },
15
+ { label: "Handlebars", value: "handlebars" },
16
+ { label: "Markdown", value: "markdown" },
17
+ { label: "Marko", value: "marko" },
18
+ { label: "React", value: "react" },
19
+ { label: "Solid", value: "solid" },
20
+ { label: "Svelte", value: "svelte" },
21
+ { label: "Voby", value: "voby" },
22
+ { label: "Vue", value: "vue" },
23
+ { label: "Web Components", value: "webc" },
24
+ ];
25
+ const BACKEND_OPTIONS = [
26
+ { label: "Go", value: "go" },
27
+ { label: "Python", value: "python" },
28
+ { label: "Ruby", value: "ruby" },
29
+ { label: "Grain", value: "grain" },
30
+ ];
31
+ const DATABASE_OPTIONS = [
32
+ { label: "SQLite", value: "sqlite" },
33
+ { label: "PostgreSQL", value: "postgresql" },
34
+ { label: "MySQL", value: "mysql" },
35
+ { label: "MongoDB", value: "mongodb" },
36
+ { label: "SurrealDB", value: "surrealdb" },
37
+ ];
38
+ // peer deps per frontend (npm names)
39
+ const FRONTEND_PEER_DEPS = {
40
+ angular: [],
41
+ eta: [],
42
+ html: [],
43
+ htmx: [],
44
+ handlebars: [],
45
+ markdown: [],
46
+ marko: [],
47
+ react: ["react", "react-dom"],
48
+ solid: ["solid-js"],
49
+ svelte: ["svelte"],
50
+ voby: [],
51
+ vue: ["vue"],
52
+ "webc": [],
53
+ };
54
+ export default async function init() {
55
+ intro("Create a new Primate app");
56
+ let directory;
57
+ let target;
58
+ while (true) {
59
+ const ans = await text({ message: "Directory to create app?", initial: "." });
60
+ if (typeof ans === "symbol" || isCancel(ans))
61
+ return cancel("Aborted.");
62
+ target = new FileRef(ans);
63
+ if (await empty(target)) {
64
+ directory = ans;
65
+ break; // Valid directory found, exit loop
66
+ }
67
+ // Directory not empty, show error but continue loop
68
+ console.log("Directory not empty, choose another.");
69
+ }
70
+ // frontends — Enter = skip (none)
71
+ const fronts = await multiselect({
72
+ message: "Choose frontend (press Enter to skip)",
73
+ options: FRONTEND_OPTIONS,
74
+ initial: [], // indices
75
+ });
76
+ if (typeof fronts === "symbol" || isCancel(fronts))
77
+ return cancel("Aborted.");
78
+ // backends — Enter = skip (none)
79
+ const backs = await multiselect({
80
+ message: "Choose backend (press Enter to skip)",
81
+ options: BACKEND_OPTIONS,
82
+ initial: [], // indices
83
+ });
84
+ if (typeof backs === "symbol" || isCancel(backs))
85
+ return cancel("Aborted.");
86
+ // runtime (must choose one)
87
+ const runtime = await select({
88
+ message: "Choose runtime",
89
+ options: [
90
+ { label: "Node", value: "node" },
91
+ { label: "Deno", value: "deno" },
92
+ { label: "Bun", value: "bun" },
93
+ ],
94
+ initial: 0,
95
+ });
96
+ if (typeof runtime === "symbol" || isCancel(runtime))
97
+ return cancel("Aborted.");
98
+ // database — Enter = skip (none); if multiple chosen, take the first
99
+ const dbChoices = await multiselect({
100
+ message: "Choose a database (press Enter to skip)",
101
+ options: DATABASE_OPTIONS,
102
+ initial: [], // indices
103
+ });
104
+ if (typeof dbChoices === "symbol" || isCancel(dbChoices))
105
+ return cancel("Aborted.");
106
+ const db = dbChoices[0];
107
+ // i18n
108
+ const i18n = await select({
109
+ message: "Enable i18n?",
110
+ options: [
111
+ { label: "Yes", value: "yes" },
112
+ { label: "No", value: "no" },
113
+ ],
114
+ initial: 1,
115
+ });
116
+ if (typeof i18n === "symbol" || isCancel(i18n))
117
+ return cancel("Aborted.");
118
+ const withI18n = i18n === "yes";
119
+ // sessions
120
+ const sessions = await select({
121
+ message: "Configure sessions?",
122
+ options: [
123
+ { label: "Yes", value: "yes" },
124
+ { label: "No", value: "no" },
125
+ ],
126
+ initial: 1,
127
+ });
128
+ if (typeof sessions === "symbol" || isCancel(sessions))
129
+ return cancel("Aborted.");
130
+ const withSessions = sessions === "yes";
131
+ // scaffold dirs
132
+ await target.create({ recursive: true });
133
+ await target.join("routes").create({ recursive: true });
134
+ await target.join("components").create({ recursive: true });
135
+ if (db)
136
+ await target.join("stores").create({ recursive: true });
137
+ // files
138
+ await gitignore(target);
139
+ await tsconfig_json(target);
140
+ await app_config(target, { fronts, backs, runtime });
141
+ if (withI18n)
142
+ await i18n_config(target);
143
+ if (withSessions)
144
+ await session_config(target);
145
+ if (db)
146
+ await database_config(target, db);
147
+ await package_json(target, { directory, runtime });
148
+ const packages = compute_packages({ fronts, backs, db });
149
+ const install = buildInstallCommand(runtime, packages, directory);
150
+ outro([
151
+ "Done, now run",
152
+ `\n ${install.print}`,
153
+ ].join("\n"));
154
+ process.exit();
155
+ }
156
+ async function empty(directory) {
157
+ try {
158
+ if (!(await directory.exists()))
159
+ return true;
160
+ const entries = await directory.list();
161
+ return entries.length === 0;
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ }
167
+ async function gitignore(root) {
168
+ const gi = root.join(".gitignore");
169
+ await gi.directory.create({ recursive: true });
170
+ const content = [
171
+ "node_modules",
172
+ "build",
173
+ "dist",
174
+ ".DS_Store",
175
+ "*.log",
176
+ "npm-debug.log*",
177
+ "yarn-debug.log*",
178
+ "yarn-error.log*",
179
+ "pnpm-debug.log*",
180
+ "",
181
+ ].join("\n");
182
+ await gi.write(content);
183
+ }
184
+ async function app_config(root, c) {
185
+ const cfg = root.join("config").join("app.ts");
186
+ await cfg.directory.create({ recursive: true });
187
+ const frontendImports = c.fronts
188
+ .map((f) => `import ${toIdent(f)} from "@primate/${f}";`)
189
+ .join("\n");
190
+ const backendImports = c.backs
191
+ .map((b) => `import ${toIdent(b)} from "@primate/${b}";`)
192
+ .join("\n");
193
+ const modules = [
194
+ ...c.fronts.map((f) => `${toIdent(f)}()`),
195
+ ...c.backs.map((b) => `${toIdent(b)}()`),
196
+ ];
197
+ const body = dedent `import config from "primate/config";
198
+ ${frontendImports}
199
+ ${backendImports}
200
+
201
+ export default config({
202
+ runtime: "${c.runtime}",
203
+ modules: [
204
+ ${modules.join(",\n ")}
205
+ ],
206
+ });
207
+ `;
208
+ await cfg.write(body);
209
+ }
210
+ // i18n scaffold: config + a default locale file
211
+ async function i18n_config(root) {
212
+ const locales = root.join("locales");
213
+ const en_us = locales.join("en-US.ts");
214
+ const i18i = root.join("config").join("i18n.ts");
215
+ await en_us.directory.create({ recursive: true });
216
+ await i18i.directory.create({ recursive: true });
217
+ const locale = `import locale from "primate/i18n/locale";
218
+
219
+ export default locale({
220
+ hi: "Hello",
221
+ placeheld: "Hello, {name}",
222
+ });
223
+ `;
224
+ await en_us.write(locale);
225
+ const config = `import en from "#locale/en-US";
226
+ import i18n from "primate/config/i18n";
227
+
228
+ export default i18n({
229
+ defaultLocale: "en-US",
230
+ locales: {
231
+ "en-US": en,
232
+ },
233
+ });
234
+ `;
235
+ await i18i.write(config);
236
+ }
237
+ async function session_config(root) {
238
+ const file = root.join("config").join("session.ts");
239
+ await file.directory.create({ recursive: true });
240
+ const body = `import session from "primate/config/session";
241
+
242
+ export default session({});
243
+ `;
244
+ await file.write(body);
245
+ }
246
+ async function database_config(root, db) {
247
+ const file = root.join("config").join("database").join("index.ts");
248
+ await file.directory.create({ recursive: true });
249
+ const ident = toIdent(db);
250
+ const body = `import ${ident} from "@primate/${db}";
251
+
252
+ export default ${ident}();
253
+ `;
254
+ await file.write(body);
255
+ }
256
+ async function package_json(root, c) {
257
+ const pkgFile = root.join("package.json");
258
+ const pkg = {
259
+ name: safe(c.directory),
260
+ type: "module",
261
+ scripts: {},
262
+ };
263
+ if (c.runtime === "deno") {
264
+ pkg.scripts.start = "deno run -A npm:primate";
265
+ pkg.scripts.build = "deno run -A npm:primate build";
266
+ pkg.scripts.serve = "deno run -A npm:primate serve";
267
+ pkg.scripts.dev = "deno task start";
268
+ }
269
+ else if (c.runtime === "bun") {
270
+ pkg.scripts.start = "bunx --bun primate";
271
+ pkg.scripts.build = "bunx --bun primate build";
272
+ pkg.scripts.serve = "bunx --bun primate serve";
273
+ pkg.scripts.dev = "bun run start";
274
+ }
275
+ else {
276
+ pkg.scripts.start = "npx primate";
277
+ pkg.scripts.build = "npx primate build";
278
+ pkg.scripts.serve = "npx primate serve";
279
+ pkg.scripts.dev = "npm run start";
280
+ }
281
+ await pkgFile.writeJSON(pkg);
282
+ }
283
+ function safe(s) {
284
+ return s.trim().toLowerCase().replace(/\s+/g, "-")
285
+ .replace(/[^a-z0-9._-]/g, "") || "primate-app";
286
+ }
287
+ function toIdent(token) {
288
+ // turn tokens like "web-components" into valid identifiers: "web_components"
289
+ return token.replace(/[^a-zA-Z0-9_$]/g, "_");
290
+ }
291
+ function compute_packages(args) {
292
+ const deps = new Set();
293
+ const devDeps = new Set();
294
+ deps.add("primate");
295
+ // Always add TypeScript as dev dependency
296
+ devDeps.add("typescript");
297
+ // frontends → @primate/<token> (+ peer deps)
298
+ for (const f of args.fronts) {
299
+ deps.add(`@primate/${f}`);
300
+ const extras = FRONTEND_PEER_DEPS[f] || [];
301
+ for (const extra of extras)
302
+ deps.add(extra);
303
+ }
304
+ // backends → @primate/<token>
305
+ for (const b of args.backs) {
306
+ deps.add(`@primate/${b}`);
307
+ }
308
+ // database → @primate/<token>, if selected
309
+ if (args.db)
310
+ deps.add(`@primate/${args.db}`);
311
+ return {
312
+ dependencies: Array.from(deps),
313
+ devDependencies: Array.from(devDeps),
314
+ };
315
+ }
316
+ function shQuote(p) {
317
+ // POSIX shell-safe quoting
318
+ return `'${p.replace(/'/g, "'\\''")}'`;
319
+ }
320
+ function buildInstallCommand(runtime, packages, dir) {
321
+ const { dependencies, devDependencies } = packages;
322
+ const allPkgs = [...dependencies, ...devDependencies];
323
+ if (allPkgs.length === 0) {
324
+ return { print: "No packages to install.", run: "" };
325
+ }
326
+ const cd = `cd ${shQuote(dir)} && `;
327
+ if (runtime === "bun") {
328
+ const depCmd = dependencies.length > 0 ? `bun add ${dependencies.join(" ")}` : "";
329
+ const devCmd = devDependencies.length > 0 ? `bun add -d ${devDependencies.join(" ")}` : "";
330
+ const commands = [depCmd, devCmd].filter(Boolean);
331
+ return { print: cd + commands.join(" && "), run: "" };
332
+ }
333
+ if (runtime === "deno") {
334
+ const inner = `deno add ${allPkgs.map((p) => `npm:${p}`).join(" ")}`;
335
+ return { print: cd + inner, run: "" };
336
+ }
337
+ // default: Node
338
+ const depCmd = dependencies.length > 0 ? `npm install ${dependencies.join(" ")}` : "";
339
+ const devCmd = devDependencies.length > 0 ? `npm install -D ${devDependencies.join(" ")}` : "";
340
+ const commands = [depCmd, devCmd].filter(Boolean);
341
+ return { print: cd + commands.join(" && "), run: "" };
342
+ }
343
+ async function tsconfig_json(root) {
344
+ await root.join("tsconfig.json").writeJSON({
345
+ extends: "primate/tsconfig",
346
+ });
347
+ }
348
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1,3 @@
1
+ declare const _default: () => Promise<any>;
2
+ export default _default;
3
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1,13 @@
1
+ import FileRef from "@rcompat/fs/FileRef";
2
+ import root from "@rcompat/package/root";
3
+ const load = async () => {
4
+ try {
5
+ return await root();
6
+ }
7
+ catch {
8
+ return FileRef.resolve();
9
+ }
10
+ };
11
+ // serve from build directory
12
+ export default async () => (await load()).join("./build/serve.js").import();
13
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1,3 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
3
+ //# sourceMappingURL=test.d.ts.map
@@ -0,0 +1,133 @@
1
+ import { tests } from "#test";
2
+ import build from "@primate/core/build";
3
+ import green from "@rcompat/cli/color/green";
4
+ import red from "@rcompat/cli/color/red";
5
+ import root from "@rcompat/package/root";
6
+ import entries from "@rcompat/record/entries";
7
+ import equals from "@rcompat/test/equals";
8
+ import includes from "@rcompat/test/includes";
9
+ import serve from "./serve.js";
10
+ const directory = "test";
11
+ const fetch_options = { redirect: "manual" };
12
+ const first_error = (left, right) => {
13
+ const length = left.length > right.length ? right.length : left.length;
14
+ for (let i = 0; i < length; i++) {
15
+ if (left[i] !== right[i]) {
16
+ return i;
17
+ }
18
+ }
19
+ };
20
+ export default async () => {
21
+ await build("testing", "web");
22
+ const app = (await serve()).default;
23
+ const files = await (await root()).join(directory)
24
+ .list(({ path }) => path.endsWith(".ts") || path.endsWith(".js"));
25
+ // side effects
26
+ await Promise.all(files.map(file => file.import()));
27
+ for (const test of tests) {
28
+ let init;
29
+ let path;
30
+ const route = test.route;
31
+ if (typeof route === "string") {
32
+ path = route;
33
+ init = new Request(`${app.url}${route}`);
34
+ }
35
+ else {
36
+ path = route.url.replace(app.url, "");
37
+ init = new Request(route.url, {
38
+ body: route.body,
39
+ // @ts-expect-error nonsense
40
+ duplex: "half",
41
+ headers: route.headers,
42
+ method: route.method,
43
+ });
44
+ }
45
+ const response = await fetch(init, fetch_options);
46
+ const checks = [];
47
+ const mocked_response = {
48
+ body: {
49
+ equals(expected) {
50
+ checks.push(async () => {
51
+ const actual = await (typeof expected === "string"
52
+ ? response.text()
53
+ : response.json());
54
+ return [equals(actual, expected), expected, actual];
55
+ });
56
+ },
57
+ includes(expected) {
58
+ checks.push(async () => {
59
+ const actual = await (typeof expected === "string"
60
+ ? response.text()
61
+ : response.json());
62
+ const $expected = typeof expected === "string"
63
+ ? expected.replaceAll("\n", "").replaceAll(" ", "")
64
+ : expected;
65
+ return [includes(actual, $expected), $expected, actual];
66
+ });
67
+ },
68
+ },
69
+ headers: {
70
+ get(name) {
71
+ const actual = response.headers.get(name);
72
+ return {
73
+ equals(expected) {
74
+ checks.push(() => {
75
+ return [equals(actual, expected), expected, actual];
76
+ });
77
+ },
78
+ includes(expected) {
79
+ checks.push(() => {
80
+ return [includes(actual, expected), expected, actual];
81
+ });
82
+ },
83
+ };
84
+ },
85
+ includes(expected) {
86
+ checks.push(() => {
87
+ const actual = Object.fromEntries(response.headers.entries());
88
+ const lowercased = entries(expected)
89
+ .keymap(([key]) => key.toLowerCase()).get();
90
+ return [includes(actual, lowercased), lowercased, actual];
91
+ });
92
+ },
93
+ },
94
+ status: {
95
+ equals(status) {
96
+ checks.push(() => {
97
+ return [response.status === status, status, response.status];
98
+ });
99
+ },
100
+ },
101
+ };
102
+ test.tester(mocked_response);
103
+ const results = await Promise.all(checks.map(async (check) => {
104
+ try {
105
+ return await check();
106
+ }
107
+ catch (error) {
108
+ console.log(error);
109
+ return [
110
+ false, "()", "test execution failed",
111
+ ];
112
+ }
113
+ }));
114
+ const failed = results.find(result => !result[0]);
115
+ const verb = test.verb.toUpperCase();
116
+ if (failed !== undefined) {
117
+ const routeText = typeof test.route === "string"
118
+ ? test.route
119
+ : new URL(test.route.url).pathname;
120
+ console.log(red(`${verb} ${routeText}`));
121
+ const expected = JSON.stringify(failed[1]);
122
+ const actual = JSON.stringify(failed[2]);
123
+ const n = first_error(expected, actual);
124
+ console.log(`expected: ${expected.slice(0, n)}${green(expected[n])}${expected.slice(n + 1)}`);
125
+ console.log(`actual: ${actual.slice(0, n)}${red(actual[n])}${actual.slice(n + 1)}`);
126
+ }
127
+ else {
128
+ console.log(green(`${verb} ${path}`));
129
+ }
130
+ }
131
+ await app.stop();
132
+ };
133
+ //# sourceMappingURL=test.js.map
package/lib/init.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ declare const _default: (...args: string[]) => Promise<void>;
2
+ export default _default;
3
+ //# sourceMappingURL=init.d.ts.map
package/lib/init.js ADDED
@@ -0,0 +1,12 @@
1
+ import blue from "@rcompat/cli/color/blue";
2
+ import bold from "@rcompat/cli/color/bold";
3
+ import print from "@rcompat/cli/print";
4
+ import json from "@rcompat/package/json";
5
+ import find from "./commands/index.js";
6
+ export default async (...args) => {
7
+ const [command, ...flags] = args;
8
+ const { name, version, } = await (await json(import.meta.url)).json();
9
+ print(blue(bold(name)), blue(version), "\n");
10
+ find(command)(flags);
11
+ };
12
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1,32 @@
1
+ import verbs from "@primate/core/request/verbs";
2
+ import type Dict from "@rcompat/type/Dict";
3
+ import type JSONValue from "@rcompat/type/JSONValue";
4
+ export type Body = JSONValue;
5
+ export type MockedResponse = {
6
+ body: {
7
+ equals(body: Body): void;
8
+ includes(body: Body): void;
9
+ };
10
+ headers: {
11
+ get(header: string): {
12
+ equals(value: string): void;
13
+ includes(value: string): void;
14
+ };
15
+ includes(headers: Dict<string>): void;
16
+ };
17
+ status: {
18
+ equals(status: number): void;
19
+ };
20
+ };
21
+ type Verb = typeof verbs[number];
22
+ type Tester = (response: MockedResponse) => void;
23
+ type Route = string;
24
+ type Test = {
25
+ route: Request | Route;
26
+ tester: Tester;
27
+ verb: Verb;
28
+ };
29
+ export declare const tests: Test[];
30
+ declare const _default: { [K in Verb]: (path: Request | Route, tester: Tester) => void; };
31
+ export default _default;
32
+ //# sourceMappingURL=test.d.ts.map