@ontrails/trails 1.0.0-beta.21 → 1.0.0-beta.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/package.json +14 -14
- package/src/release/check.ts +14 -4
- package/src/trails/add-surface.ts +2 -1
- package/src/trails/add-verify.ts +2 -1
- package/src/trails/create-scaffold.ts +107 -92
- package/src/trails/scaffold-json.ts +58 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# trails
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.23
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7c037a0: Allow `trails release check` to pass as a no-op in generated single-package apps
|
|
8
|
+
that do not declare package workspaces.
|
|
9
|
+
- Updated dependencies [9c5ecdc]
|
|
10
|
+
- @ontrails/http@1.0.0-beta.23
|
|
11
|
+
- @ontrails/commander@1.0.0-beta.23
|
|
12
|
+
- @ontrails/adapter-kit@1.0.0-beta.23
|
|
13
|
+
- @ontrails/cli@1.0.0-beta.23
|
|
14
|
+
- @ontrails/core@1.0.0-beta.23
|
|
15
|
+
- @ontrails/mcp@1.0.0-beta.23
|
|
16
|
+
- @ontrails/observe@1.0.0-beta.23
|
|
17
|
+
- @ontrails/permits@1.0.0-beta.23
|
|
18
|
+
- @ontrails/topographer@1.0.0-beta.23
|
|
19
|
+
- @ontrails/tracing@1.0.0-beta.23
|
|
20
|
+
- @ontrails/warden@1.0.0-beta.23
|
|
21
|
+
- @ontrails/wayfinder@1.0.0-beta.23
|
|
22
|
+
|
|
23
|
+
## 1.0.0-beta.22
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- cdee4d0: Emit formatter-clean fresh scaffold files so generated apps pass their own
|
|
28
|
+
`format:check` script before any manual cleanup.
|
|
29
|
+
- @ontrails/commander@1.0.0-beta.22
|
|
30
|
+
- @ontrails/adapter-kit@1.0.0-beta.22
|
|
31
|
+
- @ontrails/cli@1.0.0-beta.22
|
|
32
|
+
- @ontrails/core@1.0.0-beta.22
|
|
33
|
+
- @ontrails/http@1.0.0-beta.22
|
|
34
|
+
- @ontrails/mcp@1.0.0-beta.22
|
|
35
|
+
- @ontrails/observe@1.0.0-beta.22
|
|
36
|
+
- @ontrails/permits@1.0.0-beta.22
|
|
37
|
+
- @ontrails/topographer@1.0.0-beta.22
|
|
38
|
+
- @ontrails/tracing@1.0.0-beta.22
|
|
39
|
+
- @ontrails/warden@1.0.0-beta.22
|
|
40
|
+
- @ontrails/wayfinder@1.0.0-beta.22
|
|
41
|
+
|
|
3
42
|
## 1.0.0-beta.21
|
|
4
43
|
|
|
5
44
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/trails",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.23",
|
|
4
4
|
"bin": {
|
|
5
5
|
"trails": "./bin/trails.ts"
|
|
6
6
|
},
|
|
@@ -27,23 +27,23 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@clack/prompts": "^1.1.0",
|
|
30
|
-
"@ontrails/adapter-kit": "^1.0.0-beta.
|
|
31
|
-
"@ontrails/cli": "^1.0.0-beta.
|
|
32
|
-
"@ontrails/commander": "^1.0.0-beta.
|
|
33
|
-
"@ontrails/core": "^1.0.0-beta.
|
|
34
|
-
"@ontrails/http": "^1.0.0-beta.
|
|
35
|
-
"@ontrails/mcp": "^1.0.0-beta.
|
|
36
|
-
"@ontrails/observe": "^1.0.0-beta.
|
|
37
|
-
"@ontrails/permits": "^1.0.0-beta.
|
|
38
|
-
"@ontrails/topographer": "^1.0.0-beta.
|
|
39
|
-
"@ontrails/tracing": "^1.0.0-beta.
|
|
40
|
-
"@ontrails/warden": "^1.0.0-beta.
|
|
41
|
-
"@ontrails/wayfinder": "^1.0.0-beta.
|
|
30
|
+
"@ontrails/adapter-kit": "^1.0.0-beta.23",
|
|
31
|
+
"@ontrails/cli": "^1.0.0-beta.23",
|
|
32
|
+
"@ontrails/commander": "^1.0.0-beta.23",
|
|
33
|
+
"@ontrails/core": "^1.0.0-beta.23",
|
|
34
|
+
"@ontrails/http": "^1.0.0-beta.23",
|
|
35
|
+
"@ontrails/mcp": "^1.0.0-beta.23",
|
|
36
|
+
"@ontrails/observe": "^1.0.0-beta.23",
|
|
37
|
+
"@ontrails/permits": "^1.0.0-beta.23",
|
|
38
|
+
"@ontrails/topographer": "^1.0.0-beta.23",
|
|
39
|
+
"@ontrails/tracing": "^1.0.0-beta.23",
|
|
40
|
+
"@ontrails/warden": "^1.0.0-beta.23",
|
|
41
|
+
"@ontrails/wayfinder": "^1.0.0-beta.23",
|
|
42
42
|
"commander": "^14.0.3",
|
|
43
43
|
"typescript": "^5.9.3",
|
|
44
44
|
"zod": "^4.3.5"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@ontrails/testing": "^1.0.0-beta.
|
|
47
|
+
"@ontrails/testing": "^1.0.0-beta.23"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/src/release/check.ts
CHANGED
|
@@ -170,7 +170,7 @@ export const discoverWorkspaces = async (
|
|
|
170
170
|
const root = await readJson<PackageJson>(join(repoRoot, 'package.json'));
|
|
171
171
|
|
|
172
172
|
if (!root.workspaces || root.workspaces.length === 0) {
|
|
173
|
-
|
|
173
|
+
return [];
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
const dirs = await discoverWorkspaceDirs(repoRoot, root.workspaces);
|
|
@@ -756,9 +756,19 @@ export const runReleaseCheck = async (
|
|
|
756
756
|
const baseRef =
|
|
757
757
|
options.baseRef ??
|
|
758
758
|
(options.changedFilesPath === undefined ? 'origin/main' : undefined);
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
759
|
+
let changedFiles: readonly string[];
|
|
760
|
+
|
|
761
|
+
if (options.changedFilesPath !== undefined) {
|
|
762
|
+
changedFiles = readChangedFiles(options.changedFilesPath);
|
|
763
|
+
} else if (workspaces.length > 0) {
|
|
764
|
+
changedFiles = readLocalChangedFiles(
|
|
765
|
+
options.repoRoot,
|
|
766
|
+
baseRef ?? 'origin/main'
|
|
767
|
+
);
|
|
768
|
+
} else {
|
|
769
|
+
changedFiles = [];
|
|
770
|
+
}
|
|
771
|
+
|
|
762
772
|
const loadedConfig = await loadReleaseConfig({
|
|
763
773
|
...(options.configPath === undefined
|
|
764
774
|
? {}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from '../project-writes.js';
|
|
18
18
|
import { ontrailsPackageRange } from '../versions.js';
|
|
19
19
|
import { findTopoPath } from './project.js';
|
|
20
|
+
import { stringifyScaffoldPackageJson } from './scaffold-json.js';
|
|
20
21
|
|
|
21
22
|
type Surface = 'cli' | 'http' | 'mcp';
|
|
22
23
|
|
|
@@ -107,7 +108,7 @@ const updatePkgJsonForSurface = async (
|
|
|
107
108
|
const written = await writeProjectFile(
|
|
108
109
|
cwd,
|
|
109
110
|
'package.json',
|
|
110
|
-
|
|
111
|
+
stringifyScaffoldPackageJson(pkg)
|
|
111
112
|
);
|
|
112
113
|
return written.isErr() ? Result.err(written.error) : Result.ok(depName);
|
|
113
114
|
};
|
package/src/trails/add-verify.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
ontrailsPackageRange,
|
|
20
20
|
scaffoldDependencyVersions,
|
|
21
21
|
} from '../versions.js';
|
|
22
|
+
import { stringifyScaffoldPackageJson } from './scaffold-json.js';
|
|
22
23
|
|
|
23
24
|
// ---------------------------------------------------------------------------
|
|
24
25
|
// Content generators
|
|
@@ -81,7 +82,7 @@ const updatePackageJsonForVerify = async (
|
|
|
81
82
|
const written = await writeProjectFile(
|
|
82
83
|
projectDir,
|
|
83
84
|
'package.json',
|
|
84
|
-
|
|
85
|
+
stringifyScaffoldPackageJson(pkg)
|
|
85
86
|
);
|
|
86
87
|
return written.isErr() ? Result.err(written.error) : Result.ok();
|
|
87
88
|
};
|
|
@@ -25,6 +25,10 @@ import {
|
|
|
25
25
|
scaffoldDependencyVersions,
|
|
26
26
|
trailsPackageVersion,
|
|
27
27
|
} from '../versions.js';
|
|
28
|
+
import {
|
|
29
|
+
stringifyScaffoldJson,
|
|
30
|
+
stringifyScaffoldPackageJson,
|
|
31
|
+
} from './scaffold-json.js';
|
|
28
32
|
|
|
29
33
|
// ---------------------------------------------------------------------------
|
|
30
34
|
// Types
|
|
@@ -96,55 +100,45 @@ const generatePackageJson = (name: string): string => {
|
|
|
96
100
|
version: '0.1.0',
|
|
97
101
|
};
|
|
98
102
|
|
|
99
|
-
return
|
|
103
|
+
return stringifyScaffoldPackageJson(pkg);
|
|
100
104
|
};
|
|
101
105
|
|
|
102
106
|
const generateScaffoldProvenance = (starter: Starter): string =>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
rootDir: 'src',
|
|
123
|
-
skipLibCheck: true,
|
|
124
|
-
strict: true,
|
|
125
|
-
target: 'ESNext',
|
|
126
|
-
verbatimModuleSyntax: true,
|
|
127
|
-
},
|
|
128
|
-
include: ['src'],
|
|
107
|
+
stringifyScaffoldJson({
|
|
108
|
+
generatedAt: new Date().toISOString(),
|
|
109
|
+
scaffoldVersion: trailsPackageVersion,
|
|
110
|
+
schemaVersion: 1,
|
|
111
|
+
template: starter,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const TSCONFIG_CONTENT = `{
|
|
115
|
+
"compilerOptions": {
|
|
116
|
+
"declaration": true,
|
|
117
|
+
"module": "ESNext",
|
|
118
|
+
"moduleResolution": "bundler",
|
|
119
|
+
"noUncheckedIndexedAccess": true,
|
|
120
|
+
"outDir": "dist",
|
|
121
|
+
"rootDir": "src",
|
|
122
|
+
"skipLibCheck": true,
|
|
123
|
+
"strict": true,
|
|
124
|
+
"target": "ESNext",
|
|
125
|
+
"verbatimModuleSyntax": true
|
|
129
126
|
},
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const TSCONFIG_TESTS_CONTENT =
|
|
135
|
-
{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
types: ['bun'],
|
|
140
|
-
},
|
|
141
|
-
exclude: [],
|
|
142
|
-
extends: './tsconfig.json',
|
|
143
|
-
include: ['src', '__tests__'],
|
|
127
|
+
"include": ["src"]
|
|
128
|
+
}
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
const TSCONFIG_TESTS_CONTENT = `{
|
|
132
|
+
"compilerOptions": {
|
|
133
|
+
"noEmit": true,
|
|
134
|
+
"rootDir": ".",
|
|
135
|
+
"types": ["bun"]
|
|
144
136
|
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
"exclude": [],
|
|
138
|
+
"extends": "./tsconfig.json",
|
|
139
|
+
"include": ["src", "__tests__"]
|
|
140
|
+
}
|
|
141
|
+
`;
|
|
148
142
|
|
|
149
143
|
const AGENTS_CONTENT = `# AGENTS.md
|
|
150
144
|
|
|
@@ -226,7 +220,16 @@ export default defineConfig({
|
|
|
226
220
|
`;
|
|
227
221
|
|
|
228
222
|
const OXFMTRC_CONTENT = `{
|
|
229
|
-
|
|
223
|
+
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
|
224
|
+
"tabWidth": 2,
|
|
225
|
+
"useTabs": false,
|
|
226
|
+
"semi": true,
|
|
227
|
+
"singleQuote": true,
|
|
228
|
+
"trailingComma": "es5",
|
|
229
|
+
"bracketSpacing": true,
|
|
230
|
+
"arrowParens": "always",
|
|
231
|
+
"proseWrap": "never",
|
|
232
|
+
"printWidth": 80,
|
|
230
233
|
}
|
|
231
234
|
`;
|
|
232
235
|
|
|
@@ -235,6 +238,10 @@ const generateHelloTrail = (): string =>
|
|
|
235
238
|
import { z } from 'zod';
|
|
236
239
|
|
|
237
240
|
export const hello = trail('hello', {
|
|
241
|
+
blaze: (input) => {
|
|
242
|
+
const name = input.name ?? 'world';
|
|
243
|
+
return Result.ok({ message: \`Hello, \${name}!\` });
|
|
244
|
+
},
|
|
238
245
|
description: 'Say hello',
|
|
239
246
|
examples: [
|
|
240
247
|
{
|
|
@@ -248,17 +255,13 @@ export const hello = trail('hello', {
|
|
|
248
255
|
name: 'Named greeting',
|
|
249
256
|
},
|
|
250
257
|
],
|
|
251
|
-
blaze: (input) => {
|
|
252
|
-
const name = input.name ?? 'world';
|
|
253
|
-
return Result.ok({ message: \`Hello, \${name}!\` });
|
|
254
|
-
},
|
|
255
258
|
input: z.object({
|
|
256
259
|
name: z.string().optional(),
|
|
257
260
|
}),
|
|
261
|
+
intent: 'read',
|
|
258
262
|
output: z.object({
|
|
259
263
|
message: z.string(),
|
|
260
264
|
}),
|
|
261
|
-
intent: 'read',
|
|
262
265
|
});
|
|
263
266
|
`;
|
|
264
267
|
|
|
@@ -276,14 +279,6 @@ const entitySchema = z.object({
|
|
|
276
279
|
});
|
|
277
280
|
|
|
278
281
|
export const show = trail('entity.show', {
|
|
279
|
-
description: 'Show an entity by ID',
|
|
280
|
-
examples: [
|
|
281
|
-
{
|
|
282
|
-
expected: { id: '1', name: 'Example' },
|
|
283
|
-
input: { id: '1' },
|
|
284
|
-
name: 'Show entity',
|
|
285
|
-
},
|
|
286
|
-
],
|
|
287
282
|
blaze: (input, ctx) => {
|
|
288
283
|
const store = entityStore.from(ctx);
|
|
289
284
|
const entity = store.get(input.id);
|
|
@@ -292,13 +287,27 @@ export const show = trail('entity.show', {
|
|
|
292
287
|
}
|
|
293
288
|
return Result.ok(entity);
|
|
294
289
|
},
|
|
290
|
+
description: 'Show an entity by ID',
|
|
291
|
+
examples: [
|
|
292
|
+
{
|
|
293
|
+
expected: { id: '1', name: 'Example' },
|
|
294
|
+
input: { id: '1' },
|
|
295
|
+
name: 'Show entity',
|
|
296
|
+
},
|
|
297
|
+
],
|
|
295
298
|
input: z.object({ id: z.string() }),
|
|
296
|
-
output: entitySchema,
|
|
297
299
|
intent: 'read',
|
|
300
|
+
output: entitySchema,
|
|
298
301
|
resources: [entityStore],
|
|
299
302
|
});
|
|
300
303
|
|
|
301
304
|
export const add = trail('entity.add', {
|
|
305
|
+
blaze: (input, ctx) => {
|
|
306
|
+
const store = entityStore.from(ctx);
|
|
307
|
+
const entity = { id: randomUUID(), name: input.name };
|
|
308
|
+
store.add(entity);
|
|
309
|
+
return Result.ok(entity);
|
|
310
|
+
},
|
|
302
311
|
description: 'Add a new entity',
|
|
303
312
|
examples: [
|
|
304
313
|
{
|
|
@@ -307,20 +316,18 @@ export const add = trail('entity.add', {
|
|
|
307
316
|
name: 'Add entity',
|
|
308
317
|
},
|
|
309
318
|
],
|
|
310
|
-
blaze: (input, ctx) => {
|
|
311
|
-
const store = entityStore.from(ctx);
|
|
312
|
-
const entity = { id: randomUUID(), name: input.name };
|
|
313
|
-
store.add(entity);
|
|
314
|
-
return Result.ok(entity);
|
|
315
|
-
},
|
|
316
319
|
input: z.object({ name: z.string() }),
|
|
317
|
-
output: entitySchema,
|
|
318
320
|
intent: 'write',
|
|
321
|
+
output: entitySchema,
|
|
319
322
|
permit: { scopes: ['entity:write'] },
|
|
320
323
|
resources: [entityStore],
|
|
321
324
|
});
|
|
322
325
|
|
|
323
326
|
export const list = trail('entity.list', {
|
|
327
|
+
blaze: (_input, ctx) => {
|
|
328
|
+
const store = entityStore.from(ctx);
|
|
329
|
+
return Result.ok({ entities: store.list() });
|
|
330
|
+
},
|
|
324
331
|
description: 'List entities',
|
|
325
332
|
examples: [
|
|
326
333
|
{
|
|
@@ -329,19 +336,20 @@ export const list = trail('entity.list', {
|
|
|
329
336
|
name: 'List entities',
|
|
330
337
|
},
|
|
331
338
|
],
|
|
332
|
-
blaze: (_input, ctx) => {
|
|
333
|
-
const store = entityStore.from(ctx);
|
|
334
|
-
return Result.ok({ entities: store.list() });
|
|
335
|
-
},
|
|
336
339
|
input: z.object({}),
|
|
340
|
+
intent: 'read',
|
|
337
341
|
output: z.object({
|
|
338
342
|
entities: z.array(entitySchema),
|
|
339
343
|
}),
|
|
340
|
-
intent: 'read',
|
|
341
344
|
resources: [entityStore],
|
|
342
345
|
});
|
|
343
346
|
|
|
344
347
|
export const remove = trail('entity.delete', {
|
|
348
|
+
blaze: (input, ctx) => {
|
|
349
|
+
const store = entityStore.from(ctx);
|
|
350
|
+
const deleted = store.delete(input.id);
|
|
351
|
+
return Result.ok({ deleted, id: input.id });
|
|
352
|
+
},
|
|
345
353
|
description: 'Delete an entity by ID',
|
|
346
354
|
examples: [
|
|
347
355
|
{
|
|
@@ -350,17 +358,12 @@ export const remove = trail('entity.delete', {
|
|
|
350
358
|
name: 'Delete entity',
|
|
351
359
|
},
|
|
352
360
|
],
|
|
353
|
-
blaze: (input, ctx) => {
|
|
354
|
-
const store = entityStore.from(ctx);
|
|
355
|
-
const deleted = store.delete(input.id);
|
|
356
|
-
return Result.ok({ deleted, id: input.id });
|
|
357
|
-
},
|
|
358
361
|
input: z.object({ id: z.string() }),
|
|
362
|
+
intent: 'destroy',
|
|
359
363
|
output: z.object({
|
|
360
364
|
deleted: z.boolean(),
|
|
361
365
|
id: z.string(),
|
|
362
366
|
}),
|
|
363
|
-
intent: 'destroy',
|
|
364
367
|
permit: { scopes: ['entity:write'] },
|
|
365
368
|
resources: [entityStore],
|
|
366
369
|
});
|
|
@@ -371,6 +374,9 @@ const generateSearchTrail = (): string =>
|
|
|
371
374
|
import { z } from 'zod';
|
|
372
375
|
|
|
373
376
|
export const search = trail('search', {
|
|
377
|
+
blaze: () => {
|
|
378
|
+
return Result.ok({ results: [] });
|
|
379
|
+
},
|
|
374
380
|
description: 'Search entities by query',
|
|
375
381
|
examples: [
|
|
376
382
|
{
|
|
@@ -379,14 +385,11 @@ export const search = trail('search', {
|
|
|
379
385
|
name: 'Search entities',
|
|
380
386
|
},
|
|
381
387
|
],
|
|
382
|
-
blaze: () => {
|
|
383
|
-
return Result.ok({ results: [] });
|
|
384
|
-
},
|
|
385
388
|
input: z.object({ query: z.string() }),
|
|
389
|
+
intent: 'read',
|
|
386
390
|
output: z.object({
|
|
387
391
|
results: z.array(z.object({ id: z.string(), name: z.string() })),
|
|
388
392
|
}),
|
|
389
|
-
intent: 'read',
|
|
390
393
|
});
|
|
391
394
|
`;
|
|
392
395
|
|
|
@@ -395,8 +398,6 @@ const generateOnboardTrail = (): string =>
|
|
|
395
398
|
import { z } from 'zod';
|
|
396
399
|
|
|
397
400
|
export const onboard = trail('entity.onboard', {
|
|
398
|
-
description: 'Onboard a new entity end-to-end',
|
|
399
|
-
composes: ['entity.add'],
|
|
400
401
|
blaze: async (input, ctx) => {
|
|
401
402
|
const result = await ctx.compose('entity.add', { name: input.name });
|
|
402
403
|
if (result.isErr()) {
|
|
@@ -404,9 +405,11 @@ export const onboard = trail('entity.onboard', {
|
|
|
404
405
|
}
|
|
405
406
|
return Result.ok({ onboarded: true });
|
|
406
407
|
},
|
|
408
|
+
composes: ['entity.add'],
|
|
409
|
+
description: 'Onboard a new entity end-to-end',
|
|
407
410
|
input: z.object({ name: z.string() }),
|
|
408
|
-
output: z.object({ onboarded: z.boolean() }),
|
|
409
411
|
intent: 'write',
|
|
412
|
+
output: z.object({ onboarded: z.boolean() }),
|
|
410
413
|
permit: { scopes: ['entity:write'] },
|
|
411
414
|
});
|
|
412
415
|
`;
|
|
@@ -458,14 +461,14 @@ export const createEntityStore = (
|
|
|
458
461
|
return store.get(id);
|
|
459
462
|
},
|
|
460
463
|
list() {
|
|
461
|
-
return
|
|
464
|
+
return [...store.values()];
|
|
462
465
|
},
|
|
463
466
|
};
|
|
464
467
|
};
|
|
465
468
|
|
|
466
469
|
export const entityStore = resource('entity.store', {
|
|
467
|
-
description: 'In-memory entity store for the entity starter.',
|
|
468
470
|
create: () => Result.ok(createEntityStore()),
|
|
471
|
+
description: 'In-memory entity store for the entity starter.',
|
|
469
472
|
mock: createEntityStore,
|
|
470
473
|
});
|
|
471
474
|
`;
|
|
@@ -491,19 +494,31 @@ const starterImports: Record<
|
|
|
491
494
|
},
|
|
492
495
|
};
|
|
493
496
|
|
|
497
|
+
const renderTopoExpression = (
|
|
498
|
+
appNameLiteral: string,
|
|
499
|
+
modules: readonly string[]
|
|
500
|
+
): string => {
|
|
501
|
+
if (modules.length === 0) {
|
|
502
|
+
return `topo(${appNameLiteral})`;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (modules.length === 1) {
|
|
506
|
+
return `topo(${appNameLiteral}, ${modules[0]})`;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return `topo(\n ${[appNameLiteral, ...modules].join(',\n ')}\n)`;
|
|
510
|
+
};
|
|
511
|
+
|
|
494
512
|
const generateAppTs = (name: string, starter: Starter): string => {
|
|
495
513
|
const { imports, modules } = starterImports[starter];
|
|
496
|
-
const appNameLiteral =
|
|
497
|
-
const
|
|
498
|
-
modules.length > 0
|
|
499
|
-
? `${appNameLiteral}, ${modules.join(', ')}`
|
|
500
|
-
: appNameLiteral;
|
|
514
|
+
const appNameLiteral = `'${name}'`;
|
|
515
|
+
const topoExpression = renderTopoExpression(appNameLiteral, modules);
|
|
501
516
|
|
|
502
517
|
return [
|
|
503
518
|
"import { topo } from '@ontrails/core';",
|
|
504
519
|
...imports,
|
|
505
520
|
'',
|
|
506
|
-
`export const app =
|
|
521
|
+
`export const app = ${topoExpression};`,
|
|
507
522
|
'',
|
|
508
523
|
].join('\n');
|
|
509
524
|
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const packageKeyOrder = [
|
|
2
|
+
'name',
|
|
3
|
+
'version',
|
|
4
|
+
'bin',
|
|
5
|
+
'type',
|
|
6
|
+
'scripts',
|
|
7
|
+
'dependencies',
|
|
8
|
+
'devDependencies',
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
const packageMapKeys = new Set<string>([
|
|
12
|
+
'bin',
|
|
13
|
+
'dependencies',
|
|
14
|
+
'devDependencies',
|
|
15
|
+
'scripts',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export type ScaffoldPackageJson = Record<string, unknown>;
|
|
19
|
+
|
|
20
|
+
const isPlainRecord = (value: unknown): value is Record<string, unknown> =>
|
|
21
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
22
|
+
|
|
23
|
+
const sortRecord = (record: Record<string, unknown>): Record<string, unknown> =>
|
|
24
|
+
Object.fromEntries(
|
|
25
|
+
Object.entries(record).toSorted(([left], [right]) =>
|
|
26
|
+
left.localeCompare(right)
|
|
27
|
+
)
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const normalizePackageValue = (key: string, value: unknown): unknown =>
|
|
31
|
+
packageMapKeys.has(key) && isPlainRecord(value) ? sortRecord(value) : value;
|
|
32
|
+
|
|
33
|
+
export const normalizeScaffoldPackageJson = (
|
|
34
|
+
pkg: ScaffoldPackageJson
|
|
35
|
+
): ScaffoldPackageJson => {
|
|
36
|
+
const normalized: ScaffoldPackageJson = {};
|
|
37
|
+
|
|
38
|
+
for (const key of packageKeyOrder) {
|
|
39
|
+
if (pkg[key] !== undefined) {
|
|
40
|
+
normalized[key] = normalizePackageValue(key, pkg[key]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const key of Object.keys(pkg).toSorted()) {
|
|
45
|
+
if (!(key in normalized)) {
|
|
46
|
+
normalized[key] = normalizePackageValue(key, pkg[key]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return normalized;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const stringifyScaffoldJson = (value: unknown): string =>
|
|
54
|
+
`${JSON.stringify(value, null, 2)}\n`;
|
|
55
|
+
|
|
56
|
+
export const stringifyScaffoldPackageJson = (
|
|
57
|
+
pkg: ScaffoldPackageJson
|
|
58
|
+
): string => stringifyScaffoldJson(normalizeScaffoldPackageJson(pkg));
|