@bleedingdev/modern-js-create 3.2.0-ultramodern.16 → 3.2.0-ultramodern.18

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/dist/index.js CHANGED
@@ -1855,6 +1855,7 @@ function createTemplateManifest(modernVersion, packageSource) {
1855
1855
  targetRoot: 'generated-project-root',
1856
1856
  allowedPaths: [
1857
1857
  '.agents/**',
1858
+ '.github/**',
1858
1859
  '.modernjs/**',
1859
1860
  'AGENTS.md',
1860
1861
  'README.md',
@@ -1871,7 +1872,6 @@ function createTemplateManifest(modernVersion, packageSource) {
1871
1872
  ],
1872
1873
  deniedPaths: [
1873
1874
  '.git/**',
1874
- '.github/**',
1875
1875
  '.npmrc',
1876
1876
  '.yarnrc',
1877
1877
  '.env',
@@ -1919,6 +1919,7 @@ function createTemplateManifest(modernVersion, packageSource) {
1919
1919
  ],
1920
1920
  postMaterializationValidation: [
1921
1921
  'ultramodern-workspace-contract-check',
1922
+ 'github-workflow-security-enforced',
1922
1923
  'pnpm-11-policy-enforced',
1923
1924
  'template-manifest-retained'
1924
1925
  ],
@@ -2259,6 +2260,7 @@ function createBuiltinTemplateManifest(version) {
2259
2260
  postMaterializationValidation: [
2260
2261
  'ultramodern-contract-check',
2261
2262
  'dependency-install-with-lifecycle-deny',
2263
+ 'github-workflow-security-enforced',
2262
2264
  'package-source-retained',
2263
2265
  'pnpm-11-policy-enforced',
2264
2266
  'rstest-smoke-tests',
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.2.0-ultramodern.16",
24
+ "version": "3.2.0-ultramodern.18",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/index.js",
27
27
  "bin": {
@@ -41,7 +41,7 @@
41
41
  "@types/node": "^25.8.0",
42
42
  "@typescript/native-preview": "7.0.0-dev.20260516.1",
43
43
  "tsx": "^4.22.0",
44
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.16"
44
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.17"
45
45
  },
46
46
  "publishConfig": {
47
47
  "registry": "https://registry.npmjs.org/",
@@ -54,6 +54,6 @@
54
54
  "start": "node ./dist/index.js"
55
55
  },
56
56
  "ultramodern": {
57
- "frameworkVersion": "3.2.0-ultramodern.16"
57
+ "frameworkVersion": "3.2.0-ultramodern.17"
58
58
  }
59
59
  }
@@ -0,0 +1,53 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": [
4
+ "config:recommended",
5
+ "helpers:pinGitHubActionDigests"
6
+ ],
7
+ "dependencyDashboard": true,
8
+ "minimumReleaseAge": "1 day",
9
+ "prConcurrentLimit": 5,
10
+ "prHourlyLimit": 2,
11
+ "rangeStrategy": "bump",
12
+ "schedule": [
13
+ "before 5am on monday"
14
+ ],
15
+ "timezone": "Etc/UTC",
16
+ "packageRules": [
17
+ {
18
+ "matchManagers": [
19
+ "github-actions"
20
+ ],
21
+ "groupName": "github-actions",
22
+ "labels": [
23
+ "dependencies",
24
+ "github-actions",
25
+ "security"
26
+ ]
27
+ },
28
+ {
29
+ "matchManagers": [
30
+ "npm"
31
+ ],
32
+ "matchUpdateTypes": [
33
+ "patch",
34
+ "minor"
35
+ ],
36
+ "groupName": "npm minor and patch updates",
37
+ "labels": [
38
+ "dependencies",
39
+ "npm"
40
+ ]
41
+ },
42
+ {
43
+ "matchUpdateTypes": [
44
+ "major"
45
+ ],
46
+ "dependencyDashboardApproval": true,
47
+ "labels": [
48
+ "dependencies",
49
+ "major"
50
+ ]
51
+ }
52
+ ]
53
+ }
@@ -4,27 +4,49 @@ on:
4
4
  push:
5
5
  pull_request:
6
6
 
7
+ permissions:
8
+ contents: read
9
+
10
+ defaults:
11
+ run:
12
+ shell: bash
13
+
14
+ concurrency:
15
+ group: ultramodern-gates-${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
7
18
  jobs:
8
19
  ultramodern-gates:
9
20
  runs-on: ubuntu-latest
21
+ timeout-minutes: 20
10
22
  steps:
23
+ - name: Harden Runner
24
+ uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
25
+ with:
26
+ egress-policy: audit
27
+
11
28
  - name: Checkout
12
- uses: actions/checkout@v4
29
+ uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
30
+ with:
31
+ fetch-depth: 1
32
+ persist-credentials: false
13
33
 
14
34
  - name: Setup pnpm
15
- uses: pnpm/action-setup@v4
35
+ uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
16
36
 
17
37
  - name: Setup Node.js
18
- uses: actions/setup-node@v4
38
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
19
39
  with:
20
40
  node-version: 20
21
41
  cache: pnpm
22
42
 
23
43
  - name: Install Dependencies
24
- run: pnpm install
44
+ run: pnpm install --frozen-lockfile
25
45
 
26
46
  - name: Validate Ultramodern Contract
27
47
  run: pnpm run ultramodern:check
28
48
 
29
49
  - name: Build
50
+ env:
51
+ MODERN_PUBLIC_SITE_URL: http://localhost:8080
30
52
  run: pnpm run build
@@ -36,9 +36,13 @@ MODERN_BASELINE_ENABLE_BFF_REQUEST_ID=false
36
36
  MODERN_BASELINE_ENABLE_TELEMETRY_EXPORTERS=false
37
37
  ```
38
38
 
39
- The generated starter also includes `.github/workflows/ultramodern-gates.yml`.
40
- That workflow runs `pnpm run ultramodern:check` and `pnpm run build` on every
41
- push and pull request so the `presetUltramodern(...)` contract stays explicit.
39
+ The generated starter also includes `.github/workflows/ultramodern-gates.yml`
40
+ and `.github/renovate.json`. The workflow runs `pnpm run ultramodern:check` and
41
+ `pnpm run build` on every push and pull request with read-only permissions,
42
+ commit-pinned actions, frozen installs, and StepSecurity audit-mode runner
43
+ hardening. Renovate is configured for dependency dashboard review, one-day
44
+ release age, grouped updates, action digest pinning, and manual approval for
45
+ major upgrades.
42
46
 
43
47
  ## Micro Vertical Workspaces
44
48
 
@@ -45,7 +45,6 @@
45
45
  {{#if enableTailwind}}
46
46
  "@tailwindcss/postcss": "^4.1.18",
47
47
  {{/if}}
48
- "@testing-library/jest-dom": "^6.9.1",
49
48
  "@types/node": "^20",
50
49
  "@types/react": "^19.1.8",
51
50
  "@types/react-dom": "^19.1.6",
@@ -4,5 +4,4 @@ import { defineConfig } from '@rstest/core';
4
4
  export default defineConfig({
5
5
  extends: withModernConfig(),
6
6
  testEnvironment: 'happy-dom',
7
- setupFiles: ['./tests/rstest.setup.ts'],
8
7
  });
@@ -52,6 +52,7 @@ const requiredDeniedPaths = [
52
52
  const requiredPostMaterialization = [
53
53
  'ultramodern-contract-check',
54
54
  'dependency-install-with-lifecycle-deny',
55
+ 'github-workflow-security-enforced',
55
56
  'package-source-retained',
56
57
  'pnpm-11-policy-enforced',
57
58
  'rstest-smoke-tests',
@@ -61,6 +62,7 @@ const requiredPaths = [
61
62
  {{#unless isSubproject}}
62
63
  'AGENTS.md',
63
64
  '.agents/skills-lock.json',
65
+ '.github/renovate.json',
64
66
  '.github/workflows/ultramodern-gates.yml',
65
67
  'oxlint.config.ts',
66
68
  'oxfmt.config.ts',
@@ -74,7 +76,6 @@ const requiredPaths = [
74
76
  'config/public/locales/cs/translation.json',
75
77
  'src/modern-app-env.d.ts',
76
78
  'src/routes/[lang]/page.tsx',
77
- 'tests/rstest.setup.ts',
78
79
  'tests/ultramodern.contract.test.ts',
79
80
  ];
80
81
  const manifestErrors = [];
@@ -91,6 +92,53 @@ if (fs.existsSync(path.resolve(process.cwd(), 'src/routes/page.tsx'))) {
91
92
  process.exit(1);
92
93
  }
93
94
 
95
+ {{#unless isSubproject}}
96
+ const workflowContent = fs.readFileSync(
97
+ path.resolve(process.cwd(), '.github/workflows/ultramodern-gates.yml'),
98
+ 'utf-8',
99
+ );
100
+ const renovateConfig = JSON.parse(
101
+ fs.readFileSync(path.resolve(process.cwd(), '.github/renovate.json'), 'utf-8'),
102
+ );
103
+ for (const requiredSnippet of [
104
+ 'permissions:\n contents: read',
105
+ 'pull_request:',
106
+ 'persist-credentials: false',
107
+ 'pnpm install --frozen-lockfile',
108
+ 'MODERN_PUBLIC_SITE_URL: http://localhost:8080',
109
+ 'timeout-minutes:',
110
+ 'egress-policy: audit',
111
+ ]) {
112
+ if (!workflowContent.includes(requiredSnippet)) {
113
+ console.error(`Generated workflow must retain ${requiredSnippet.split('\n')[0]}`);
114
+ process.exit(1);
115
+ }
116
+ }
117
+ if (workflowContent.includes('pull_request_target')) {
118
+ console.error('Generated workflow must not use pull_request_target');
119
+ process.exit(1);
120
+ }
121
+ for (const match of workflowContent.matchAll(/^\s*uses:\s*([^@\s]+)@([^\s#]+)/gmu)) {
122
+ const [, actionName, actionRef] = match;
123
+ if (!/^[a-f0-9]{40}$/u.test(actionRef)) {
124
+ console.error(`Generated workflow must pin ${actionName}@${actionRef} to a commit SHA`);
125
+ process.exit(1);
126
+ }
127
+ }
128
+ if (
129
+ renovateConfig.dependencyDashboard !== true ||
130
+ renovateConfig.minimumReleaseAge !== '1 day' ||
131
+ !renovateConfig.extends?.includes('helpers:pinGitHubActionDigests') ||
132
+ !renovateConfig.packageRules?.some(
133
+ (rule) =>
134
+ rule.dependencyDashboardApproval === true && rule.matchUpdateTypes?.includes('major'),
135
+ )
136
+ ) {
137
+ console.error('Generated Renovate config must retain dashboard, release-age, action pinning, and major-approval policy');
138
+ process.exit(1);
139
+ }
140
+ {{/unless}}
141
+
94
142
  const pageContent = fs.readFileSync(
95
143
  path.resolve(process.cwd(), 'src/routes/[lang]/page.tsx'),
96
144
  'utf-8',
@@ -358,7 +406,6 @@ for (const dependency of [
358
406
  '@effect/tsgo',
359
407
  '@modern-js/adapter-rstest',
360
408
  '@rstest/core',
361
- '@testing-library/jest-dom',
362
409
  '@typescript/native-preview',
363
410
  'happy-dom',
364
411
  {{#unless isSubproject}}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "extends": "../tsconfig.json",
3
3
  "compilerOptions": {
4
- "types": ["@testing-library/jest-dom", "@rstest/core/globals"]
4
+ "types": ["@rstest/core/globals"]
5
5
  },
6
6
  "include": ["./"]
7
7
  }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": ["config:recommended", "helpers:pinGitHubActionDigests"],
4
+ "dependencyDashboard": true,
5
+ "minimumReleaseAge": "1 day",
6
+ "prConcurrentLimit": 5,
7
+ "prHourlyLimit": 2,
8
+ "rangeStrategy": "bump",
9
+ "schedule": ["before 5am on monday"],
10
+ "timezone": "Etc/UTC",
11
+ "packageRules": [
12
+ {
13
+ "matchManagers": ["github-actions"],
14
+ "groupName": "github-actions",
15
+ "labels": ["dependencies", "github-actions", "security"]
16
+ },
17
+ {
18
+ "matchManagers": ["npm"],
19
+ "matchUpdateTypes": ["patch", "minor"],
20
+ "groupName": "npm minor and patch updates",
21
+ "labels": ["dependencies", "npm"]
22
+ },
23
+ {
24
+ "matchUpdateTypes": ["major"],
25
+ "dependencyDashboardApproval": true,
26
+ "labels": ["dependencies", "major"]
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,52 @@
1
+ name: Ultramodern Workspace Gates
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ defaults:
11
+ run:
12
+ shell: bash
13
+
14
+ concurrency:
15
+ group: ultramodern-workspace-gates-${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ jobs:
19
+ ultramodern-workspace-gates:
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 30
22
+ steps:
23
+ - name: Harden Runner
24
+ uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
25
+ with:
26
+ egress-policy: audit
27
+
28
+ - name: Checkout
29
+ uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
30
+ with:
31
+ fetch-depth: 1
32
+ persist-credentials: false
33
+
34
+ - name: Setup pnpm
35
+ uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
36
+
37
+ - name: Setup Node.js
38
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
39
+ with:
40
+ node-version: 20
41
+ cache: pnpm
42
+
43
+ - name: Install Dependencies
44
+ run: pnpm install --frozen-lockfile
45
+
46
+ - name: Validate Ultramodern Workspace Contract
47
+ run: pnpm run ultramodern:check
48
+
49
+ - name: Build Workspace Apps
50
+ env:
51
+ MODERN_PUBLIC_SITE_URL: http://localhost:8080
52
+ run: pnpm -r --filter "./apps/**" run build
@@ -19,7 +19,12 @@ Run the scaffold validator before adding business code:
19
19
  pnpm ultramodern:check
20
20
  ```
21
21
 
22
- The topology and ownership metadata are generated under `topology/`.
22
+ The topology and ownership metadata are generated under `topology/`. The
23
+ workspace also ships `.github/workflows/ultramodern-workspace-gates.yml` and
24
+ `.github/renovate.json` with read-only workflow permissions, commit-pinned
25
+ actions, frozen installs, StepSecurity audit-mode runner hardening, dependency
26
+ dashboard review, one-day release age, grouped updates, and manual approval for
27
+ major upgrades.
23
28
 
24
29
  Package source metadata is generated at
25
30
  `.modernjs/ultramodern-package-source.json`. The default strategy keeps
@@ -41,6 +41,8 @@ const requiredPaths = [
41
41
  'tsconfig.base.json',
42
42
  'oxlint.config.ts',
43
43
  'oxfmt.config.ts',
44
+ '.github/renovate.json',
45
+ '.github/workflows/ultramodern-workspace-gates.yml',
44
46
  '.agents/skills-lock.json',
45
47
  '.agents/rstackjs-agent-skills-LICENSE',
46
48
  '.agents/skills/rsbuild-best-practices/SKILL.md',
@@ -121,6 +123,8 @@ const rootPackage = readJson('package.json');
121
123
  const packageSource = readJson('.modernjs/ultramodern-package-source.json');
122
124
  const skillsLock = readJson('.agents/skills-lock.json');
123
125
  const pnpmWorkspace = readText('pnpm-workspace.yaml');
126
+ const workflowContent = readText('.github/workflows/ultramodern-workspace-gates.yml');
127
+ const renovateConfig = readJson('.github/renovate.json');
124
128
  const deprecatedTanstackRuntime = '@modern-js/runtime/' + 'tanstack-router';
125
129
  const expectedModernSpecifier =
126
130
  packageSource.strategy === 'install' ? packageSource.modernPackages?.specifier : 'workspace:*';
@@ -164,6 +168,42 @@ assert(
164
168
  !pnpmWorkspace.includes('onlyBuiltDependencies'),
165
169
  'pnpm-workspace.yaml must use pnpm 11 allowBuilds instead of onlyBuiltDependencies',
166
170
  );
171
+ for (const requiredSnippet of [
172
+ 'permissions:\n contents: read',
173
+ 'pull_request:',
174
+ 'persist-credentials: false',
175
+ 'pnpm install --frozen-lockfile',
176
+ 'pnpm -r --filter "./apps/**" run build',
177
+ 'MODERN_PUBLIC_SITE_URL: http://localhost:8080',
178
+ 'timeout-minutes:',
179
+ 'egress-policy: audit',
180
+ ]) {
181
+ assert(
182
+ workflowContent.includes(requiredSnippet),
183
+ `Generated workspace workflow must retain ${requiredSnippet.split('\n')[0]}`,
184
+ );
185
+ }
186
+ assert(
187
+ !workflowContent.includes('pull_request_target'),
188
+ 'Generated workspace workflow must not use pull_request_target',
189
+ );
190
+ for (const match of workflowContent.matchAll(/^\s*uses:\s*([^@\s]+)@([^\s#]+)/gmu)) {
191
+ const [, actionName, actionRef] = match;
192
+ assert(
193
+ /^[a-f0-9]{40}$/u.test(actionRef),
194
+ `Generated workspace workflow must pin ${actionName}@${actionRef} to a commit SHA`,
195
+ );
196
+ }
197
+ assert(
198
+ renovateConfig.dependencyDashboard === true &&
199
+ renovateConfig.minimumReleaseAge === '1 day' &&
200
+ renovateConfig.extends?.includes('helpers:pinGitHubActionDigests') &&
201
+ renovateConfig.packageRules?.some(
202
+ (rule) =>
203
+ rule.dependencyDashboardApproval === true && rule.matchUpdateTypes?.includes('major'),
204
+ ),
205
+ 'Generated workspace Renovate config must retain dashboard, release-age, action pinning, and major-approval policy',
206
+ );
167
207
  assert(
168
208
  rootPackage.modernjs?.packageSource?.config === './.modernjs/ultramodern-package-source.json',
169
209
  'Root must point to the UltraModern package source metadata',
@@ -506,5 +546,9 @@ assert(
506
546
  manifest.validation?.postMaterializationValidation?.includes('pnpm-11-policy-enforced'),
507
547
  'Template manifest must document pnpm 11 policy validation',
508
548
  );
549
+ assert(
550
+ manifest.validation?.postMaterializationValidation?.includes('github-workflow-security-enforced'),
551
+ 'Template manifest must document generated workflow security validation',
552
+ );
509
553
 
510
554
  console.log('UltraModern workspace scaffold validated');
@@ -1,4 +0,0 @@
1
- import { expect } from '@rstest/core';
2
- import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
3
-
4
- expect.extend(jestDomMatchers);