@synopackageland/templates 0.40.2 → 0.40.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.40.3
4
+
5
+ - Use CLI architecture names in local package tests and SDK constants in background-limit assertions
6
+ - Exercise earlier request deadlines in journal-lock tests without waiting for the SDK's full operation allowance
7
+ - Install scaffold test and build tools with `--include=dev`, including when `NODE_ENV=production`
8
+ - Explain the upstream-web-only `ui: false` layout, retained entry-gate files and removable browser examples
9
+ - Smoke-test freshly generated projects for every template and native UI variant with the current CLI
10
+
3
11
  ## 0.40.2
4
12
 
5
13
  - Include independent examples and npm scripts for local unit and registry contracts, local package checks, real-NAS ctl, deterministic UI outcomes and optional AI actions
package/README.md CHANGED
@@ -216,8 +216,18 @@ scripts/assemble.sh --language rust /tmp/app-rs plain jobs # python, node or ru
216
216
  scripts/gate.sh # layout, every combination, binary, npm pack
217
217
  scripts/gate.sh --fast # only the combinations the changes reach
218
218
  scripts/assemble.sh --list vue jobs # the templates a combination uses
219
+ SYNOPKGLAND_CLI="$(command -v synopkgland)" node --test scripts/test/scaffold.test.mjs
219
220
  ```
220
221
 
222
+ The gate installs the CLI range declared by the shared scaffold into its temporary
223
+ directory, or uses the executable in `SYNOPKGLAND_CLI`. Its existing `node:test`
224
+ harness runs real CLI init for every template and both native UI variants,
225
+ then each generated project's default `npm test`. Native projects assert the
226
+ starter operation; binary and Compose report the expected skip because they
227
+ have no starter registry. One plain native project also runs its generated
228
+ `test:local` check/build/inspect tier. Backend tests and Vue builds stay in the
229
+ existing combination checks; Rust builds and frontend checks run on the Docker host.
230
+
221
231
  Without `SYNOPKGLAND_SDK_DIR` the combinations use the released SDK that
222
232
  `backend/go.mod` and `frontend/package.json` pin. With it, `assemble.sh` adds
223
233
  `replace github.com/synopackageland/syno-pkg-land-sdk => $SYNOPKGLAND_SDK_DIR`
@@ -347,7 +357,10 @@ when an upstream application needs its public port in its own configuration.
347
357
  ## App-open smoke
348
358
 
349
359
  Every generated project includes `tests/e2e/smoke.e2e.ts`, `e2e.config.ts` and
350
- a root `package.json`. Run `npm install` at the project root, install Chromium once with
360
+ a root `package.json`. Run `npm install --include=dev` at the project root
361
+ (or `npm ci --include=dev` after committing its lockfile).
362
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
363
+ Install Chromium once with
351
364
  `npx @e2e-dev/web install chromium`, then run `npm run e2e:smoke`. Set `DSM_E2E_SSH` and private mode-600 account files in
352
365
  `DSM_E2E_ACCOUNT_ADMIN` and `DSM_E2E_ACCOUNT_NORMAL`; grant the normal account
353
366
  Application Privileges first. Missing normal credentials/profile produces a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synopackageland/templates",
3
- "version": "0.40.2",
3
+ "version": "0.40.3",
4
4
  "description": "Project templates for synopkgland init: native-go, native-python, native-node and native-rust (plain or Vue, with jobs, delegate and root-ops overlays), binary and compose.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,7 +22,7 @@ sections at the end name each language's files, types and test commands.
22
22
 
23
23
  From the project root, run `synopkgland check --json`,
24
24
  `synopkgland build --json`, then `synopkgland inspect <spk> --json` on the emitted SPK.
25
- For Vue, first run `npm ci` and `npm run build` in frontend. Run its tests only if the user chose tests.
25
+ For Vue, first run `npm ci --include=dev` and `npm run build` in frontend. Run its tests only if the user chose tests.
26
26
  Do not connect to or change a NAS without explicit task authorization.
27
27
 
28
28
  ## Optional testing
@@ -33,6 +33,11 @@ on tests. Scaffolding is included by default; writing and running tests are opti
33
33
  Read `synopkgland skill show testing` when setting up or extending tests.
34
34
 
35
35
  The default `npm test` runs only fast local unit/ctl registry contracts.
36
+ At the project root, run `npm install --include=dev` first; after committing its
37
+ lockfile, use `npm ci --include=dev`.
38
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI and frontend builds need dev tools.
39
+ Before `test:local`, prepare the backend/frontend as described below and run
40
+ `synopkgland ctl generate` for a native App.
36
41
  `npm run test:local` opts into check/build/inspect. Only on explicit user request,
37
42
  run `npm run test:nas:ctl`, `npm run e2e:smoke`, `npm run e2e:ui` or `npm run e2e:ai`.
38
43
  The full suite is slow. Never call these from build, install, DSM hooks or release.
@@ -49,6 +54,20 @@ the cheapest adequate model and low effort; never default to an expensive model.
49
54
  Use E2E_LLM_BASE_URL, E2E_LLM_MODEL, E2E_LLM_API_KEY_FILE (mode 600) or
50
55
  E2E_LLM_API_KEY, and optional E2E_LLM_REASONING_EFFORT (default low).
51
56
 
57
+ ## Upstream web UI only
58
+
59
+ For an `--upstream-fork` wrapper whose only UI is the upstream web panel, set
60
+ `ui: false` in `synology/dsm-package.yaml` and configure `identity_gateway`
61
+ for that panel. `--ui plain` selects `ui/app.js`; it does not mean `ui: false`.
62
+ The current scaffold still creates UI and browser-test examples: remove
63
+ `synology/ui/` (or `synology/frontend/` for Vue), `synology/tests/e2e/`,
64
+ `synology/e2e.config.ts` and `synology/e2e.ai.config.ts`, and remove the
65
+ `e2e:*` scripts from `synology/package.json`.
66
+ Keep `synology/backend/`, `synology/i18n/messages/`, `synology/operations.json` and
67
+ the local/ctl tests: the entry gate still needs the backend and message catalogs.
68
+ Read `synopkgland skill show upstream-fork` and
69
+ `synopkgland skill show orchestrator` for the wrapper workflow.
70
+
52
71
  ## Lifecycle scripts (`dsm-scripts/`)
53
72
 
54
73
  `dsm-scripts/` holds DSM's own lifecycle scripts (preinst, postinst, preupgrade,
@@ -371,7 +390,7 @@ their capabilities with `synopkgland init --with`.
371
390
  `new AppError(code, message)`, `AppError.badRequest(message)`. Call services
372
391
  through `service(ctx, op, params)` (`service.ts`), which passes the runtime's
373
392
  refusals on; a bare `ctx.runtime` refusal would reach the browser as `internal`.
374
- - Test with `(cd backend && npm ci && npm test && npm run typecheck)`; `test/support.ts`'s
393
+ - Test with `(cd backend && npm ci --include=dev && npm test && npm run typecheck)`; `test/support.ts`'s
375
394
  `call` plays the runtime.
376
395
 
377
396
  ## Rust backend
@@ -6,7 +6,7 @@ import { cli } from '../support/cli.mjs';
6
6
  // Explicit local build tier; no deployment or NAS connection.
7
7
  test('the App checks, builds and passes package inspection', {timeout: 300_000}, () => {
8
8
  cli('check');
9
- const built = cli('build', '--arch', process.env.APP_TEST_ARCH || 'amd64');
9
+ const built = cli('build', '--arch', process.env.APP_TEST_ARCH || 'x86_64');
10
10
  assert.ok(built.outputs.length > 0);
11
11
  for (const spk of built.outputs) {
12
12
  assert.equal(existsSync(spk), true);
@@ -0,0 +1,16 @@
1
+ # __DISPLAY_NAME__
2
+
3
+ ## Local tests
4
+
5
+ From the project root, install the test tools before running the optional tests:
6
+
7
+ ```sh
8
+ npm install --include=dev
9
+ npm test
10
+ ```
11
+
12
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
13
+ After committing the generated root lockfile, use `npm ci --include=dev`.
14
+ The scaffold provides example prebuilt binaries; replace them with your own
15
+ Linux binaries before packaging. See `AGENTS.md` for the package workflow.
16
+
@@ -0,0 +1,16 @@
1
+ # __DISPLAY_NAME__
2
+
3
+ ## Local tests
4
+
5
+ From the project root, install the test tools before running the optional tests:
6
+
7
+ ```sh
8
+ npm install --include=dev
9
+ npm test
10
+ ```
11
+
12
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
13
+ After committing the generated root lockfile, use `npm ci --include=dev`.
14
+ Configure the upstream image and its real readiness endpoint before
15
+ `test:local`; see `AGENTS.md` for the Compose packaging workflow.
16
+
@@ -25,10 +25,25 @@ independent package-account worker and report phase/progress. Authenticate
25
25
  status/result polling by that ID; a slow NAS or failed status read leaves the
26
26
  last queued/running state intact. After a lost admission reply, reuse the same
27
27
  idempotency key and input instead of replaying a mutation with a new key.
28
- Jobs default to 30 minutes; approved executor limits remain separate. ctl keeps
28
+ Jobs use the SDK's default operation allowance; approved executor limits remain separate. ctl keeps
29
29
  its 62-second transport default; use an explicit `--timeout` or an existing
30
30
  nonblocking job. See the SDK's [request deadline contract](https://github.com/synopackageland/syno-pkg-land-sdk/blob/main/docs/request-deadlines.md).
31
31
 
32
+ ## Local tests
33
+
34
+ From the project root, install the test tools before running the optional tests:
35
+
36
+ ```sh
37
+ npm install --include=dev
38
+ npm test
39
+ ```
40
+
41
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
42
+ After committing the generated root lockfile, use `npm ci --include=dev`.
43
+ Before `npm run test:local`, prepare the backend and, for Vue, build the
44
+ frontend using the steps below and the backend section in `AGENTS.md`, then
45
+ run `synopkgland ctl generate`.
46
+
32
47
  ## Quick start
33
48
 
34
49
  Needs Node.js with `synopkgland` installed, and Go 1.26.10.
@@ -443,3 +458,16 @@ Draft writes require a non-mutating preview and explicit human confirmation.
443
458
  Long operations use the existing job flow; tools own polling and pagination.
444
459
 
445
460
  Use a matching SDK/CLI/templates release containing the registry support.
461
+
462
+ ## Upstream web UI only
463
+
464
+ `init --upstream-fork --ui plain` still creates a plain SDK UI; it does not
465
+ select `ui: false`. For an upstream web panel with no SDK UI, set `ui: false`
466
+ in `synology/dsm-package.yaml` and configure `identity_gateway` for the panel.
467
+ Remove `synology/ui/` (or `synology/frontend/` for Vue),
468
+ `synology/tests/e2e/`, `synology/e2e.config.ts` and
469
+ `synology/e2e.ai.config.ts`, and remove the `e2e:*` npm scripts.
470
+ Keep `synology/backend/`, `synology/i18n/messages/`, `synology/operations.json` and
471
+ the local/ctl tests: the entry gate still needs the backend and message catalogs.
472
+ Read `synopkgland skill show upstream-fork` and
473
+ `synopkgland skill show orchestrator` for the wrapper workflow.
@@ -34,6 +34,10 @@ func newBgEnv(t *testing.T) *bgEnv {
34
34
  }
35
35
 
36
36
  func (e *bgEnv) call(auth api.Authenticator, method, params string) (string, map[string]any) {
37
+ return e.callContext(context.Background(), auth, method, params)
38
+ }
39
+
40
+ func (e *bgEnv) callContext(ctx context.Context, auth api.Authenticator, method, params string) (string, map[string]any) {
37
41
  e.t.Helper()
38
42
  // SDK policy provides owner routes immediately.
39
43
  routes := newBackgroundRoutes(e.store, e.dataDir)
@@ -42,6 +46,7 @@ func (e *bgEnv) call(auth api.Authenticator, method, params string) (string, map
42
46
  e.t.Fatal(err)
43
47
  }
44
48
  req := httptest.NewRequest(http.MethodPost, "/api.cgi", strings.NewReader(`{"method":"`+method+`","params":`+params+`}`))
49
+ req = req.WithContext(ctx)
45
50
  req.Header.Set("Content-Type", "application/json")
46
51
  req.Header.Set("Sec-Fetch-Site", "same-origin")
47
52
  rec := httptest.NewRecorder()
@@ -112,8 +117,18 @@ func TestBackgroundStatusReportsLimitsAndWorker(t *testing.T) {
112
117
  t.Fatalf("%s %v", code, out)
113
118
  }
114
119
  limits := d["limits"].(map[string]any)
115
- if limits["maxInputBytes"].(float64) != 512*1024 || limits["maxQueued"].(float64) != 16 || limits["maxTotalBytes"].(float64) != 16<<20 || limits["jobTimeoutSeconds"].(float64) != 30*60 {
116
- t.Fatalf("limits: %v", limits)
120
+ for name, want := range map[string]float64{
121
+ "maxInputBytes": background.MaxInputBytes,
122
+ "maxQueued": background.MaxQueued,
123
+ "maxSchedules": background.MaxSchedules,
124
+ "maxTotalBytes": background.MaxTotalBytes,
125
+ "maxOwnerPending": background.MaxOwnerPending,
126
+ "maxOwnerBytes": background.MaxOwnerBytes,
127
+ "jobTimeoutSeconds": background.JobTimeout.Seconds(),
128
+ } {
129
+ if got := limits[name]; got != want {
130
+ t.Errorf("limits[%q] = %v, want %v", name, got, want)
131
+ }
117
132
  }
118
133
  info := background.WorkerInfo{PID: os.Getpid(), UID: os.Getuid(), Username: "pkg"}
119
134
  if err := e.store.Heartbeat(context.Background(), info); err != nil {
@@ -52,7 +52,7 @@ func TestBackgroundBusyErrorCode(t *testing.T) {
52
52
  }
53
53
  }
54
54
 
55
- func TestBackgroundRequestSubmitBusyAndProductionConstruction(t *testing.T) {
55
+ func TestBackgroundRequestSubmitDeadlineAndProductionConstruction(t *testing.T) {
56
56
  e := newBgEnv(t)
57
57
  dir := filepath.Join(t.TempDir(), "background")
58
58
  e.store = background.NewRequestStore(dir)
@@ -68,12 +68,17 @@ func TestBackgroundRequestSubmitBusyAndProductionConstruction(t *testing.T) {
68
68
  t.Fatal(err)
69
69
  }
70
70
  defer syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
71
+ // Use an earlier caller deadline instead of waiting for the SDK's full
72
+ // operation allowance. The API reports that deadline, not jobs_busy.
73
+ budget := min(50*time.Millisecond, background.RequestLockTimeout/2)
74
+ ctx, cancel := context.WithTimeout(context.Background(), budget)
75
+ defer cancel()
71
76
  start := time.Now()
72
- code, out := e.call(alice, "bg.jobs.submit", e.submitParams("k1"))
73
- if code != "jobs_busy" || time.Since(start) > background.RequestLockTimeout+time.Second {
74
- t.Fatalf("busy submit: %s %+v", code, out)
77
+ code, out := e.callContext(ctx, alice, "bg.jobs.submit", e.submitParams("k1"))
78
+ if code != "request_timeout" || time.Since(start) > budget+time.Second {
79
+ t.Fatalf("submit deadline: %s %+v", code, out)
75
80
  }
76
- // The actual production assembly must use the same bounded wait policy.
81
+ // The production route assembly must also honor an earlier request deadline.
77
82
  srv, err := api.NewServerWithPermissions(alice, []byte(`{"schema":1,"default":"administrators","methods":{}}`), BackgroundRoutes(filepath.Dir(dir))...)
78
83
  if err != nil {
79
84
  t.Fatal(err)
@@ -82,8 +87,8 @@ func TestBackgroundRequestSubmitBusyAndProductionConstruction(t *testing.T) {
82
87
  r.Header.Set("Content-Type", "application/json")
83
88
  r.Header.Set("Sec-Fetch-Site", "same-origin")
84
89
  w := httptest.NewRecorder()
85
- srv.ServeHTTP(w, r)
86
- if !strings.Contains(w.Body.String(), `"jobs_busy"`) {
90
+ api.WithRequestDeadline(srv, api.Timeout(budget)).ServeHTTP(w, r)
91
+ if !strings.Contains(w.Body.String(), `"request_timeout"`) {
87
92
  t.Fatal(w.Body.String())
88
93
  }
89
94
  }
@@ -7,9 +7,24 @@ files, portal, jobs and delegate components come from `@synopackageland/sdk/vue`
7
7
  `github.com/synopackageland/syno-pkg-land-sdk` version in `backend/go.mod`. See
8
8
  <https://github.com/synopackageland/syno-pkg-land-sdk> for the components and their props.
9
9
 
10
+ ## Local tests
11
+
12
+ From the project root, install the test tools before running the optional tests:
13
+
14
+ ```sh
15
+ npm install --include=dev
16
+ npm test
17
+ ```
18
+
19
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
20
+ After committing the generated root lockfile, use `npm ci --include=dev`.
21
+ Before `npm run test:local`, prepare the backend and, for Vue, build the
22
+ frontend using the steps below and the backend section in `AGENTS.md`, then
23
+ run `synopkgland ctl generate`.
24
+
10
25
  ```sh
11
26
  cd frontend
12
- npm ci
27
+ npm ci --include=dev
13
28
  npm test
14
29
  npm run build
15
30
  ```
@@ -25,16 +25,31 @@ independent package-account worker and report phase/progress. Authenticate
25
25
  status/result polling by that ID; a slow NAS or failed status read leaves the
26
26
  last queued/running state intact. After a lost admission reply, reuse the same
27
27
  idempotency key and input instead of replaying a mutation with a new key.
28
- Jobs default to 30 minutes; approved executor limits remain separate. ctl keeps
28
+ Jobs use the SDK's default operation allowance; approved executor limits remain separate. ctl keeps
29
29
  its 62-second transport default; use an explicit `--timeout` or an existing
30
30
  nonblocking job. See the SDK's [request deadline contract](https://github.com/synopackageland/syno-pkg-land-sdk/blob/main/docs/request-deadlines.md).
31
31
 
32
+ ## Local tests
33
+
34
+ From the project root, install the test tools before running the optional tests:
35
+
36
+ ```sh
37
+ npm install --include=dev
38
+ npm test
39
+ ```
40
+
41
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
42
+ After committing the generated root lockfile, use `npm ci --include=dev`.
43
+ Before `npm run test:local`, prepare the backend and, for Vue, build the
44
+ frontend using the steps below and the backend section in `AGENTS.md`, then
45
+ run `synopkgland ctl generate`.
46
+
32
47
  ## Quick start
33
48
 
34
49
  Needs Node.js 20 or later with `synopkgland` installed. The SPK depends on DSM's **Node.js v20** package (INFO `install_dep_packages`).
35
50
 
36
51
  ```bash
37
- (cd backend && npm ci && npm test && npm run build) # method tests, then app/*.js
52
+ (cd backend && npm ci --include=dev && npm test && npm run build) # method tests, then app/*.js
38
53
  synopkgland ctl generate
39
54
  synopkgland check
40
55
  synopkgland build # → dist/__PACKAGE__-0.1.0-0001-{x86_64,aarch64}.spk
@@ -42,7 +42,7 @@ arches: [x86_64, aarch64]
42
42
  # and app/permissions.json, then runs `node main.js` in backend/app as the
43
43
  # package account, once per call, with Package Center's Node.js (node.package
44
44
  # becomes an install dependency). backend/src compiles to backend/app: run
45
- # `npm ci` and `npm run build` in backend before check and build. sdk is the
45
+ # `npm ci --include=dev` and `npm run build` in backend before check and build. sdk is the
46
46
  # SDK release whose runtime and Node SDK (node_modules/@synopackageland/app)
47
47
  # the build ships; the build reads the methods from `main.js --describe`.
48
48
  backend:
@@ -7,9 +7,24 @@ delegate components come from `@synopackageland/sdk/vue` (types from
7
7
  `@synopackageland/sdk`); its version must equal the `backend.sdk` in dsm-package.yaml. See
8
8
  <https://github.com/synopackageland/syno-pkg-land-sdk> for the components and their props.
9
9
 
10
+ ## Local tests
11
+
12
+ From the project root, install the test tools before running the optional tests:
13
+
14
+ ```sh
15
+ npm install --include=dev
16
+ npm test
17
+ ```
18
+
19
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
20
+ After committing the generated root lockfile, use `npm ci --include=dev`.
21
+ Before `npm run test:local`, prepare the backend and, for Vue, build the
22
+ frontend using the steps below and the backend section in `AGENTS.md`, then
23
+ run `synopkgland ctl generate`.
24
+
10
25
  ```sh
11
26
  cd frontend
12
- npm ci
27
+ npm ci --include=dev
13
28
  npm test
14
29
  npm run build
15
30
  ```
@@ -25,10 +25,25 @@ independent package-account worker and report phase/progress. Authenticate
25
25
  status/result polling by that ID; a slow NAS or failed status read leaves the
26
26
  last queued/running state intact. After a lost admission reply, reuse the same
27
27
  idempotency key and input instead of replaying a mutation with a new key.
28
- Jobs default to 30 minutes; approved executor limits remain separate. ctl keeps
28
+ Jobs use the SDK's default operation allowance; approved executor limits remain separate. ctl keeps
29
29
  its 62-second transport default; use an explicit `--timeout` or an existing
30
30
  nonblocking job. See the SDK's [request deadline contract](https://github.com/synopackageland/syno-pkg-land-sdk/blob/main/docs/request-deadlines.md).
31
31
 
32
+ ## Local tests
33
+
34
+ From the project root, install the test tools before running the optional tests:
35
+
36
+ ```sh
37
+ npm install --include=dev
38
+ npm test
39
+ ```
40
+
41
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
42
+ After committing the generated root lockfile, use `npm ci --include=dev`.
43
+ Before `npm run test:local`, prepare the backend and, for Vue, build the
44
+ frontend using the steps below and the backend section in `AGENTS.md`, then
45
+ run `synopkgland ctl generate`.
46
+
32
47
  ## Quick start
33
48
 
34
49
  Needs Node.js with `synopkgland` installed, and Python 3.8 or later.
@@ -7,9 +7,24 @@ delegate components come from `@synopackageland/sdk/vue` (types from
7
7
  `@synopackageland/sdk`); its version must equal the `backend.sdk` in dsm-package.yaml. See
8
8
  <https://github.com/synopackageland/syno-pkg-land-sdk> for the components and their props.
9
9
 
10
+ ## Local tests
11
+
12
+ From the project root, install the test tools before running the optional tests:
13
+
14
+ ```sh
15
+ npm install --include=dev
16
+ npm test
17
+ ```
18
+
19
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
20
+ After committing the generated root lockfile, use `npm ci --include=dev`.
21
+ Before `npm run test:local`, prepare the backend and, for Vue, build the
22
+ frontend using the steps below and the backend section in `AGENTS.md`, then
23
+ run `synopkgland ctl generate`.
24
+
10
25
  ```sh
11
26
  cd frontend
12
- npm ci
27
+ npm ci --include=dev
13
28
  npm test
14
29
  npm run build
15
30
  ```
@@ -25,10 +25,25 @@ independent package-account worker and report phase/progress. Authenticate
25
25
  status/result polling by that ID; a slow NAS or failed status read leaves the
26
26
  last queued/running state intact. After a lost admission reply, reuse the same
27
27
  idempotency key and input instead of replaying a mutation with a new key.
28
- Jobs default to 30 minutes; approved executor limits remain separate. ctl keeps
28
+ Jobs use the SDK's default operation allowance; approved executor limits remain separate. ctl keeps
29
29
  its 62-second transport default; use an explicit `--timeout` or an existing
30
30
  nonblocking job. See the SDK's [request deadline contract](https://github.com/synopackageland/syno-pkg-land-sdk/blob/main/docs/request-deadlines.md).
31
31
 
32
+ ## Local tests
33
+
34
+ From the project root, install the test tools before running the optional tests:
35
+
36
+ ```sh
37
+ npm install --include=dev
38
+ npm test
39
+ ```
40
+
41
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
42
+ After committing the generated root lockfile, use `npm ci --include=dev`.
43
+ Before `npm run test:local`, prepare the backend and, for Vue, build the
44
+ frontend using the steps below and the backend section in `AGENTS.md`, then
45
+ run `synopkgland ctl generate`.
46
+
32
47
  ## Quick start
33
48
 
34
49
  Needs Node.js with `synopkgland` installed, Rust through rustup, Zig and `cargo-zigbuild`: `backend/rust-toolchain.toml` pins 1.97.1 with the three DSM targets, so the first `cargo` run installs them.
@@ -7,9 +7,24 @@ delegate components come from `@synopackageland/sdk/vue` (types from
7
7
  `@synopackageland/sdk`); its version must equal the `syno-pkg-land-app` tag in backend/Cargo.toml. See
8
8
  <https://github.com/synopackageland/syno-pkg-land-sdk> for the components and their props.
9
9
 
10
+ ## Local tests
11
+
12
+ From the project root, install the test tools before running the optional tests:
13
+
14
+ ```sh
15
+ npm install --include=dev
16
+ npm test
17
+ ```
18
+
19
+ `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
20
+ After committing the generated root lockfile, use `npm ci --include=dev`.
21
+ Before `npm run test:local`, prepare the backend and, for Vue, build the
22
+ frontend using the steps below and the backend section in `AGENTS.md`, then
23
+ run `synopkgland ctl generate`.
24
+
10
25
  ```sh
11
26
  cd frontend
12
- npm ci
27
+ npm ci --include=dev
13
28
  npm test
14
29
  npm run build
15
30
  ```