Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions .snyk
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ ignore:
reason: 'Transitive dependency in @azure/functions; upgrade path has type errors'
expires: '2026-07-28T00:00:00.000Z'
created: '2026-01-15T11:04:00.000Z'
'SNYK-JS-YAUZL-15467445':
- '* > yauzl@<3.2.1':
reason: 'Transitive dependency in @mongodb-memory-server; not exploitable in current usage.'
expires: '2026-03-26T00:00:00.000Z'
created: '2026-03-12T12:35:00.000Z'
'SNYK-JS-UUID-16133035':
- '* > uuid@8.3.2':
reason: 'Transitive dependency in Docusaurus and Azurite dev-only stacks; no compatible fix path available yet from upstream.'
Expand All @@ -71,11 +66,6 @@ ignore:
reason: 'Mongoose 8.22.1 has TypeScript constraint errors in type definitions (types/inferrawdoctype.d.ts, types/inferschematype.d.ts) that break compilation. Patch attempts failed due to external library type incompatibilities. Risk is Low: requires control of query field names and values.'
expires: '2026-11-07T00:00:00.000Z'
created: '2026-05-07T09:00:00.000Z'
'SNYK-JS-BABELPLUGINTRANSFORMMODULESSYSTEMJS-16624576':
- '* > @babel/plugin-transform-modules-systemjs@7.28.5':
reason: 'Transitive dependency in Docusaurus; not exploitable in current usage.'
expires: '2026-06-28T00:00:00.000Z'
created: '2026-05-11T10:00:00.000Z'
'SNYK-JS-AI-16734889':
- '@docusaurus/preset-classic@3.10.1 > * > ai@5.0.105':
reason: 'Transitive dependency in Docusaurus docsearch; Snyk reports no fixed upgrade or patch available.'
Expand All @@ -101,11 +91,6 @@ ignore:
reason: 'Transitive dependency in Docusaurus CSS optimization/build tooling; Snyk reports no fixed upgrade or patch available. Not exploitable at runtime because docs CSS is repository-controlled and processed at build time.'
expires: '2026-09-18T00:00:00.000Z'
created: '2026-06-08T00:00:00.000Z'
'SNYK-JS-SHELLQUOTE-17457810':
- '* > shell-quote@1.8.4':
reason: 'Transitive dependency in @docusaurus/core via webpack-dev-server > launch-editor; fixed in shell-quote@1.9.0 but no direct upgrade path available. Dev-time only; not exploitable in current usage.'
expires: '2026-12-31T00:00:00.000Z'
created: '2026-06-25T00:00:00.000Z'
'SNYK-JS-IMAGESIZE-17295814':
- '* > image-size':
reason: 'Transitive dependency in @docusaurus/mdx-loader and dev build tooling; no upgrade or patch available from upstream. Not exploitable in current usage (build-time image dimension parsing only).'
Expand Down
38 changes: 38 additions & 0 deletions build-pipeline/scripts/verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env node
/// <reference types="node" />

import { architectureTests, coverageMerge, e2eTests, knipCheck, pnpmAudit, pnpmScript, snykCodeScan, snykDependencyScan, sonarPullRequestAnalysis, sonarQualityGate, verificationSequence } from '@cellix/local-dev/silent-runners';

const snykOrgArgs = ['--org=cellixjs', '--remote-repo-url=https://github.com/CellixJs/cellixjs'];
const snykDependencyArgs = [...snykOrgArgs, '--all-projects', '--policy-path=.snyk', '--exclude=dist,build,.turbo,coverage,.agents-work,.agents,.claude,.github,requirements.txt'];

const cellixVerify = verificationSequence
.addStep(pnpmScript('format:check'))
.addStep(architectureTests())
.addStep(coverageMerge())
.addStep(e2eTests())
.addStep(knipCheck())
.addStep(pnpmAudit({ auditLevel: 'high', dependencyType: 'prod', name: 'audit:prod' }))
.addStep(pnpmAudit({ auditLevel: 'critical', dependencyType: 'dev', name: 'audit:dev' }))
.addStep(
snykDependencyScan({
args: snykDependencyArgs,
}),
)
.addStep(
snykCodeScan({
args: snykOrgArgs,
}),
)
.addStep(sonarPullRequestAnalysis())
.addStep(sonarQualityGate());

function runVerifyCommand(): void {
const result = cellixVerify.run();
if (result.status === 0) {
process.stdout.write('verify passed\n');
}
process.exitCode = result.status;
}

runVerifyCommand();
4 changes: 4 additions & 0 deletions knip.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"workspaces": {
".": {
"entry": ["build-pipeline/scripts/verify.ts"],
"project": ["build-pipeline/scripts/**/*.ts"]
},
"apps/api": {
"entry": ["src/index.ts", "start-*.ts", "sync-local-settings.ts", "rolldown.config.ts"],
"project": ["src/**/*.ts", "*.ts"],
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"sonar:pr": "export PR_NUMBER=$(node build-pipeline/scripts/get-pr-number.cjs) && sonar-scanner -Dsonar.pullrequest.key=$PR_NUMBER -Dsonar.pullrequest.branch=$(git branch --show-current) -Dsonar.pullrequest.base=main",
"sonar:pr-windows": "for /f %i in ('node build-pipeline/scripts/get-pr-number.cjs') do set PR_NUMBER=%i && sonar-scanner -Dsonar.pullrequest.key=%PR_NUMBER% -Dsonar.pullrequest.branch=%BRANCH_NAME% -Dsonar.pullrequest.base=main",
"check-sonar": "node build-pipeline/scripts/check-sonar-quality-gate.cjs",
"verify": "pnpm run format:check && pnpm run test:arch && pnpm run test:coverage:merge && pnpm run test:e2e:worktree && pnpm run knip && pnpm run audit && pnpm run snyk",
"verify": "node --conditions=source build-pipeline/scripts/verify.ts",
"knip": "knip",
"snyk": "pnpm run snyk:test && pnpm run snyk:code",
"snyk:report": "pnpm run snyk:monitor && pnpm run snyk:code:report",
Expand All @@ -73,6 +73,7 @@
"@ant-design/cli": "^6.3.5",
"@biomejs/biome": "2.4.10",
"@cellix/graphql-codegen": "workspace:*",
"@cellix/local-dev": "workspace:*",
"@graphql-codegen/cli": "^5.0.7",
"@graphql-codegen/introspection": "^4.0.3",
"@graphql-codegen/typed-document-node": "^5.1.2",
Expand Down
98 changes: 90 additions & 8 deletions packages/cellix/local-dev/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# @cellix/local-dev

Generic local-development helpers for Cellix app wrappers.
Generic local-development and verification helpers for Cellix app wrappers.

This package is intentionally policy-free. It owns reusable mechanics such as worktree port math, URL helpers, JSON and dotenv utilities, process exit forwarding, and generic dev runners. App-specific env keys, hostnames, auth routes, and `local.settings.json` mutations belong in app-owned wrapper scripts or internal repo helpers, not here.
This package is intentionally policy-free. It owns reusable mechanics such as worktree port math, URL helpers, JSON and dotenv utilities, process exit forwarding, generic dev runners, and silent verification-command runners. App-specific env keys, hostnames, auth routes, scanner orgs, and `local.settings.json` mutations belong in app-owned wrapper scripts or internal repo helpers, not here.

## Install

Expand Down Expand Up @@ -30,6 +30,7 @@ In this monorepo, app packages consume the workspace package directly:
- Azure Functions
- Node-backed processes
- Azurite
- Silent verification command runners and portable tool-wrapper builders

## Recommended consumption pattern

Expand Down Expand Up @@ -109,6 +110,74 @@ values, including complete URL values nested in objects or arrays. It leaves URL
embedded in descriptive text unchanged. Use `convertSettingsForWorktree` when
the application needs explicit key-level conversion policy.

## Silent runners

`runSilentCommand` captures stdout and stderr while a command is running. If the
command succeeds, nothing is printed. If it fails, whatever the command wrote to
stdout and stderr is replayed before the failing status is returned.

```js
import { runSilentCommand } from '@cellix/local-dev/silent-runners';

const result = runSilentCommand({
command: 'snyk',
args: ['test', '--all-projects'],
});

process.exitCode = result.status;
```

`result.status` is always a number, so it can be assigned straight to
`process.exitCode`. A command terminated by a signal has no exit status of its
own and is mapped to the shell convention of `128 + signalNumber` (SIGINT
becomes 130, SIGKILL becomes 137); its replayed header names the signal rather
than an exit code, and `result.signal` carries the signal itself.

Use `runSilentCommandSequence` when a wrapper needs to run several commands in
order. Steps are silent by default; mark a step with `output: 'inherit'` only
when its live output is part of the intended consumer experience.

```js
import { runSilentCommandSequence } from '@cellix/local-dev/silent-runners';

const result = runSilentCommandSequence({
steps: [
{ name: 'format:check', command: 'pnpm', args: ['run', 'format:check'] },
{ name: 'test:e2e', command: 'pnpm', args: ['run', 'test:e2e'] },
],
});

process.exitCode = result.status;
```

For reusable verification workflows, use the fluent sequence builder:

```js
import { pnpmScript, verificationSequence } from '@cellix/local-dev/silent-runners';

const verify = verificationSequence
.addStep(pnpmScript('format:check'))
.addStep(pnpmScript('test'));

const result = verify.run();
process.exitCode = result.status;
```

Prefer the named tool wrappers when a command has a known CLI shape:

```js
import { knipCheck, pnpmAudit, runSilentCommandSequence, snykCodeScan, snykDependencyScan } from '@cellix/local-dev/silent-runners';

const result = runSilentCommandSequence({
steps: [
knipCheck(),
pnpmAudit({ auditLevel: 'high', dependencyType: 'prod' }),
snykDependencyScan({ args: ['--all-projects', '--org=my-org'] }),
snykCodeScan({ args: ['--org=my-org'] }),
],
});
```

## Public API

All exports are available from `@cellix/local-dev`. Folder-level subpaths are
Expand All @@ -117,6 +186,7 @@ also published for consumers that want narrower imports:
- `@cellix/local-dev/files`
- `@cellix/local-dev/process`
- `@cellix/local-dev/runners`
- `@cellix/local-dev/silent-runners`
- `@cellix/local-dev/urls`
- `@cellix/local-dev/vite`
- `@cellix/local-dev/workspace`
Expand Down Expand Up @@ -152,15 +222,27 @@ also published for consumers that want narrower imports:
- `getMongoPort`
- `getAzuritePorts`
- `buildAzuriteConnectionString`
- `runViteDev`
- `runDocusaurusDev`
- `runAzureFunctionsDev`
- `runNodeDev`
- `runAzuriteDev`
- `runTsxDev` deprecated compatibility alias
- `runSilentCommand`
- `runSilentCommandSequence`
- `architectureTests`
- `coverageMerge`
- `e2eTests`
- `knipCheck`
- `livePnpmScript`
- `pnpmAudit`
- `pnpmScript`
- `snykCodeScan`
- `snykDependencyScan`
- `snykIacScan`
- `sonarPullRequestAnalysis`
- `sonarQualityGate`
- `VerificationSequence`
- `verificationSequence`

## Notes

- The package derives workspace roots from the caller's current working directory, but it does not infer app layouts or env-variable names.
- Worktree names are sanitized before they are inserted into `.localhost` hostnames so branch-style names such as `jason/my-feature` become DNS-safe labels such as `jason-my-feature`. Suffixing is idempotent for hostnames that already contain the sanitized worktree label.
- Silent tool wrappers encode reusable CLI shape; scripts still own project-specific arguments such as org names, paths, and CI policy.
- Captured verification output defaults to 64 MiB and can be adjusted with `maxBuffer` for a command, sequence, or individual sequence step.
- If a helper only exists to support one app's local policy, it should usually live with that app instead of being exported here.
19 changes: 14 additions & 5 deletions packages/cellix/local-dev/manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

## Purpose

Provide generic local-development primitives for Cellix app packages so app-owned wrapper scripts can supply project-specific env keys, hostnames, and local-settings policy without hardcoding those details into the shared package.
Provide generic local-development and verification primitives for Cellix app packages so app-owned wrapper scripts can supply project-specific env keys, hostnames, local-settings policy, and scanner policy without hardcoding those details into the shared package.

## Scope

This package owns generic worktree-aware port math, URL helpers, dotenv and JSON file utilities, child-process lifecycle handling, worktree settings transforms, and generic dev-runner orchestration for Vite, Docusaurus, Azure Functions, Node-backed processes, and Azurite.
This package owns generic worktree-aware port math, URL helpers, dotenv and JSON file utilities, child-process lifecycle handling, worktree settings transforms, generic dev-runner orchestration for Vite, Docusaurus, Azure Functions, Node-backed processes, and Azurite, plus silent verification runners and portable tool-wrapper builders.

## Non-goals

- Production runtime behavior
- App-specific env-variable names, hostnames, auth-provider paths, or local-settings keys
- Project-specific scanner policy such as Snyk org names, repository URLs, token handling, report publishing, or CI thresholds
- Discovering repo-specific app folders from inside the package
- Replacing app-owned wrapper scripts that express local project policy

Expand All @@ -23,6 +24,7 @@ Published entrypoints:
- `@cellix/local-dev/files`
- `@cellix/local-dev/process`
- `@cellix/local-dev/runners`
- `@cellix/local-dev/silent-runners`
- `@cellix/local-dev/urls`
- `@cellix/local-dev/vite`
- `@cellix/local-dev/workspace`
Expand All @@ -39,8 +41,8 @@ Root entrypoint exports:
- `getWorktreePortOffset(worktreeName?)`, `getMongoPort(worktreeName?)`, `getAzuritePorts(worktreeName?)`, `buildAzuriteConnectionString(options)`
- `ViteDevRunner`, `DocusaurusDevRunner`, `AzureFunctionsDevRunner`, `NodeDevRunner`, `AzuriteDevRunner`
- `WorktreeSettings`, `AzureFunctionsLocalSettings`, `resolveAzureFunctionsLocalSettingsValues`, `convertSettingsForWorktree`, `WorktreeConversionPlan`, `WorktreeMode`
- `runViteDev(options?)`, `runDocusaurusDev(options?)`, `runAzureFunctionsDev(options?)`, `runNodeDev(options?)`, `runAzuriteDev(options)`
- `runTsxDev(options?)` remains as a deprecated compatibility alias for `runNodeDev(options?)`
- `runSilentCommand(options)`, `runSilentCommandSequence(options)`, `VerificationSequence`, `verificationSequence`
- Tool-wrapper builders such as `knipCheck()`, `pnpmAudit(options)`, `snykDependencyScan(options)`, `snykCodeScan(options)`, and `sonarPullRequestAnalysis(options)`

## Core concepts

Expand All @@ -51,26 +53,33 @@ Root entrypoint exports:
- Azure Functions dev runners may prepare `local.settings.json` before startup because the Functions host reads settings from its script root rather than the process environment alone.
- Consumers that need the same Azure Functions values outside `func start` can resolve them without writing a file; regular mode remains unscoped unless a worktree name is explicitly available.
- Hostname suffixing must sanitize raw worktree names before inserting them into `.localhost` domains, and repeated suffixing with the same worktree label must leave hostnames unchanged.
- Silent verification runners capture external command output, emit nothing on success, and replay whatever the command wrote to stdout/stderr on failure.
- Silent command sequences run ordered verification steps, defaulting each step to silent output while allowing explicit passthrough where live output is intentionally part of the command contract.
- Tool wrappers encode portable CLI shapes; root or app scripts own project-specific arguments.
- Commands are spawned without shell interpolation.

## Package boundaries

- Do not encode OCOM app names, auth paths, env-variable names, or `local.settings.json` schemas in this package.
- Do not encode project-specific Snyk, Edgescan, or scanner policy here.
- Keep repo-specific hostname and env mapping logic in app-owned scripts or internal repo helpers outside this package.
- Avoid widening the public surface with one-off helpers that only exist to support a single app branch.

## Dependencies / relationships

- Downstream consumers in this monorepo: `@apps/api`, `@apps/docs`, `@apps/ui-community`, `@apps/ui-staff`, `@apps/server-oauth2-mock`, `@apps/server-mongodb-memory-mock`
- Downstream consumers in this monorepo: `@apps/api`, `@apps/docs`, `@apps/ui-community`, `@apps/ui-staff`, `@apps/server-oauth2-mock`, `@apps/server-mongodb-memory-mock`, and the root `verify` script
- Consumed from app-owned wrapper scripts and from tests through the TypeScript API

## Testing strategy

- Prefer public-entrypoint tests for dotenv parsing, URL helpers, worktree port derivation, runner object spawning, Vite arg building, Azurite connection-string building, worktree settings transforms, and generic JSON syncing.
- Public-entrypoint tests should prove silent-runner success output is suppressed, failure output is replayed, ordered sequences stop on failure, and the reusable verification sequence stays immutable.
- Avoid tests that prove repo-specific wrapper policy through this package's public contract.

## Documentation obligations

- Keep `README.md` focused on the generic helper surface and how app-owned wrappers compose it.
- Document silent verification runners as generic command wrappers, not as project-specific CI policy.
- Keep TSDoc aligned on public exports that define package behavior.
- Update this manifest when public exports or scope boundaries change.

Expand Down
10 changes: 9 additions & 1 deletion packages/cellix/local-dev/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@cellix/local-dev",
"version": "1.0.0",
"description": "Generic local-development helpers for Cellix app wrappers",
"description": "Generic local-development and verification helpers for Cellix app wrappers",
"type": "module",
"files": [
"dist",
Expand All @@ -26,10 +26,18 @@
"types": "./dist/runners/index.d.ts",
"default": "./dist/runners/index.js"
},
"./silent-runners": {
"types": "./dist/silent-runners/index.d.ts",
"default": "./dist/silent-runners/index.js"
},
"./urls": {
"types": "./dist/urls/index.d.ts",
"default": "./dist/urls/index.js"
},
"./vite": {
"types": "./dist/vite/index.d.ts",
"default": "./dist/vite/index.js"
},
"./workspace": {
"types": "./dist/workspace/index.d.ts",
"default": "./dist/workspace/index.js"
Expand Down
2 changes: 1 addition & 1 deletion packages/cellix/local-dev/src/files/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { type DotEnvValues, readDotEnv } from './dotenv.ts';
export { readJsonFile, type SyncJsonFileOptions, syncJsonFile } from './json.ts';
export { readJsonFile, type SyncJsonFileOptions, syncJsonFile, writeJsonFile } from './json.ts';
Loading
Loading