create-substrat 0.10.11 → 0.10.13

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/dev-servers.js CHANGED
@@ -24,8 +24,8 @@
24
24
  */
25
25
 
26
26
  /**
27
- * Two processes: the local OIDC issuer, and the Hono API that relies on it. There is no
28
- * web app to scaffold yet.
27
+ * Three processes: the local OIDC issuer, the Hono API that relies on it, and the test
28
+ * dashboard. There is no web app to scaffold yet.
29
29
  *
30
30
  * The issuer is a separate PROCESS rather than a branch inside the API on purpose — that
31
31
  * is what keeps the vertical free of any dev-only auth path, and lets it be swapped for a
@@ -34,4 +34,7 @@
34
34
  export const DEV_SERVERS = [
35
35
  { name: 'issuer', run: 'issuer', portEnv: 'ISSUER_PORT', portFrom: 'src/server.ts' },
36
36
  { name: 'api', run: 'server', portEnv: 'PORT', portFrom: 'src/server.ts' },
37
+ // The test dashboard (#756). 5290 — clear of the demo blocks (527x/528x/529x), which a
38
+ // scaffold has no claim on (#983).
39
+ { name: 'tests', run: 'test:ui', portEnv: 'VITEST_UI_PORT', portFrom: 'vitest.ui.config.ts' },
37
40
  ];
package/index.js CHANGED
@@ -36,11 +36,12 @@ const TEMPLATE = join(HERE, 'template');
36
36
  // The runtime packages release together off one version line (the changesets `fixed`
37
37
  // group), so one constant is right for all of them. Engines do NOT share a line —
38
38
  // each versions on its own, so one pin per engine, deliberately.
39
- const SUBSTRAT = '^0.134.0';
40
- const ENGINE_WORKORDER = '^0.12.18';
41
- const ENGINE_INVOICING = '^0.11.18';
39
+ const SUBSTRAT = '^0.136.0';
40
+ const ENGINE_WORKORDER = '^0.12.20';
41
+ const ENGINE_INVOICING = '^0.11.20';
42
42
  const BOUNDARY_LINT = '^0.6.1';
43
- const DEV_ISSUER = '^0.2.16';
43
+ const DEV_ISSUER = '^0.2.18';
44
+ const ATTACHMENT_EXTRACTORS = '^0.1.2';
44
45
 
45
46
  const DOCS = 'https://substrat.net';
46
47
 
@@ -113,6 +114,9 @@ function packageJson(name) {
113
114
  issuer: 'substrat-dev-issuer --personas src/personas.ts',
114
115
  server: 'tsx src/server.ts',
115
116
  test: 'vitest run',
117
+ // The live test dashboard in the Browser pane: the `tests` entry of
118
+ // .claude/launch.json. `--watch` is explicit, since vitest runs once and exits without a TTY.
119
+ 'test:ui': 'vitest --watch --ui --open=false -c vitest.ui.config.ts',
116
120
  typecheck: 'tsc --noEmit && tsc -p tsconfig.worker.json --noEmit',
117
121
  'lint:boundaries': 'substrat-boundary-lint',
118
122
  },
@@ -122,6 +126,7 @@ function packageJson(name) {
122
126
  '@substrat-run/adapter-sqlite': SUBSTRAT,
123
127
  '@substrat-run/adapter-cloudflare': SUBSTRAT,
124
128
  '@substrat-run/vertical-host': SUBSTRAT,
129
+ '@substrat-run/attachment-extractors': ATTACHMENT_EXTRACTORS,
125
130
  '@substrat-run/engine-workorder': ENGINE_WORKORDER,
126
131
  '@substrat-run/engine-invoicing': ENGINE_INVOICING,
127
132
  hono: '^4.6.0',
@@ -140,6 +145,7 @@ function packageJson(name) {
140
145
  tsx: '^4.19.0',
141
146
  typescript: '^5.6.0',
142
147
  vitest: '^3.0.0',
148
+ '@vitest/ui': '^3.0.0',
143
149
  },
144
150
  // Do NOT add `zod` here — import `z` from `@substrat-run/contracts` (AGENTS.md, rule 10).
145
151
  // better-sqlite3 >=13 ships prebuilt binaries for every supported platform and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-substrat",
3
- "version": "0.10.11",
3
+ "version": "0.10.13",
4
4
  "description": "Scaffold a Substrat vertical — `npm create substrat <dir>`.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -33,5 +33,6 @@
33
33
  },
34
34
  "publishConfig": {
35
35
  "access": "public"
36
- }
36
+ },
37
+ "scripts": {}
37
38
  }
@@ -20,6 +20,16 @@
20
20
  ],
21
21
  "port": 8891,
22
22
  "autoPort": false
23
+ },
24
+ {
25
+ "name": "tests",
26
+ "runtimeExecutable": "pnpm",
27
+ "runtimeArgs": [
28
+ "run",
29
+ "test:ui"
30
+ ],
31
+ "port": 5290,
32
+ "autoPort": false
23
33
  }
24
34
  ]
25
35
  }
@@ -11,6 +11,7 @@ import {
11
11
  } from '@substrat-run/contracts';
12
12
  import { ulid } from '@substrat-run/kernel';
13
13
  import { SqliteScopeHost } from '@substrat-run/adapter-sqlite';
14
+ import { defaultAttachmentExtractors } from '@substrat-run/attachment-extractors';
14
15
  import { ENTITLEMENT_KEYS, MODULES, OWNER_ROLE_KEY, portalPerms, ROLES } from './provision.js';
15
16
 
16
17
  // The provisioning surface (modules, roles, grant shapes) lives in
@@ -43,7 +44,7 @@ export interface BikeShopWorld {
43
44
  }
44
45
 
45
46
  export function buildBikeShopHost(dir: string): SqliteScopeHost {
46
- const host = new SqliteScopeHost({ dir });
47
+ const host = new SqliteScopeHost({ dir, attachmentExtractors: defaultAttachmentExtractors() });
47
48
  for (const m of MODULES) host.registerModule(m);
48
49
  return host;
49
50
  }
@@ -47,14 +47,15 @@ import {
47
47
  SCOPE_SWEEPER_NAME,
48
48
  type ScopeSweeperDo,
49
49
  } from '@substrat-run/adapter-cloudflare';
50
+ import type { ScopeStub } from '@substrat-run/kernel';
51
+ import { defaultAttachmentExtractors } from '@substrat-run/attachment-extractors';
50
52
  import {
51
53
  kickFlags,
54
+ mountPlatformSurface,
52
55
  readRoutedNode,
53
56
  RouterAssertionError,
54
- type ScopeStub,
55
57
  invocationLog,
56
- } from '@substrat-run/kernel';
57
- import { mountPlatformSurface } from '@substrat-run/vertical-host';
58
+ } from '@substrat-run/vertical-host';
58
59
  import { MODULES, OWNER_ROLE_KEY, ROLES } from './provision.js';
59
60
  import { SHOP_ENV } from './manifest.js';
60
61
  import { mountApi } from './routes.js';
@@ -146,7 +147,7 @@ function nodeFor(req: Request, env: Env): Node {
146
147
  }
147
148
 
148
149
  function hostFor(env: Env): CloudflareScopeHost {
149
- const host = new CloudflareScopeHost({ scope: env.SCOPE });
150
+ const host = new CloudflareScopeHost({ scope: env.SCOPE, attachmentExtractors: defaultAttachmentExtractors() });
150
151
  for (const m of MODULES) host.registerModule(m);
151
152
  return host;
152
153
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The config behind `pnpm test:ui` — your own `vitest.config.ts`, plus the live test
3
+ * dashboard served on a port Claude Desktop's Browser pane can open (`.claude/launch.json`,
4
+ * the `tests` entry).
5
+ *
6
+ * The UI lives at `/__vitest__/` and the bare origin is a 404, but a launch entry carries
7
+ * a port and never a path, so `/` is redirected to the UI here. `VITEST_UI_PORT` moves
8
+ * the port; it binds 127.0.0.1 only, since the dashboard can re-run your tests. The port
9
+ * is strict: a busy one fails the start rather than moving the dashboard somewhere the
10
+ * launch entry does not point.
11
+ */
12
+ import { mergeConfig, type Plugin } from 'vitest/config';
13
+
14
+ import base from './vitest.config.ts';
15
+
16
+ const redirectRootToUi: Plugin = {
17
+ name: 'substrat:vitest-ui-root',
18
+ configureServer(server) {
19
+ server.middlewares.use((req, res, next) => {
20
+ if (req.url !== '/') return next();
21
+ res.statusCode = 302;
22
+ res.setHeader('Location', '/__vitest__/');
23
+ res.end();
24
+ });
25
+ },
26
+ };
27
+
28
+ export default mergeConfig(base, {
29
+ plugins: [redirectRootToUi],
30
+ test: {
31
+ api: {
32
+ host: '127.0.0.1',
33
+ port: Number(process.env.VITEST_UI_PORT ?? 5290),
34
+ strictPort: true,
35
+ },
36
+ },
37
+ });