@bleedingdev/modern-js-create 3.5.0-ultramodern.2 → 3.5.0-ultramodern.4

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/README.md CHANGED
@@ -193,8 +193,8 @@ For strict Effect API migrations, update generated package metadata and Modern
193
193
  package aliases through the framework command before hand-editing app code:
194
194
 
195
195
  ```bash
196
- pnpm dlx @bleedingdev/modern-js-create@3.5.0-ultramodern.2 ultramodern \
197
- migrate-strict-effect --version 3.5.0-ultramodern.2
196
+ pnpm dlx @bleedingdev/modern-js-create@3.5.0-ultramodern.4 ultramodern \
197
+ migrate-strict-effect --version 3.5.0-ultramodern.4
198
198
  pnpm api:check
199
199
  pnpm contract:check
200
200
  pnpm check
@@ -202,7 +202,8 @@ pnpm build
202
202
  ```
203
203
 
204
204
  The command updates `.modernjs/ultramodern.json`, root `modernjs.packageSource`,
205
- generated Modern package aliases, old direct topology metadata, and the pnpm
205
+ generated Modern package aliases, framework-owned toolchain pins, old direct
206
+ topology metadata, strict Effect pnpm overrides/trust policy, and the pnpm
206
207
  lockfile. It does not invent compatibility shims or move business code behind
207
208
  your back. If `pnpm api:check` still fails, migrate the source to
208
209
  `shared/api.ts`, `api/index.ts`, and `src/api/*-client.ts` and delete
@@ -210,8 +211,11 @@ your back. If `pnpm api:check` still fails, migrate the source to
210
211
 
211
212
  Generated strict Effect workspaces pin the compatible Effect cohort through
212
213
  `pnpm-workspace.yaml` overrides: `effect@4.0.0-beta.91` and
213
- `@effect/vitest@4.0.0-beta.91`. Do not override those in app packages; update
214
- the framework cohort when the runtime moves.
214
+ `@effect/vitest@4.0.0-beta.91`. The generated pnpm policy also excludes the
215
+ known `effect@4.0.0-beta.91` and `@effect/opentelemetry@4.0.0-beta.91`
216
+ trusted-publisher to provenance transitions from no-downgrade checks. Do not
217
+ override those in app packages; update the framework cohort when the runtime
218
+ moves.
215
219
 
216
220
  Use `--ultramodern-package-source=install` for published cohort proof and pin a
217
221
  specific release with `--ultramodern-package-version` when CI must prove an
@@ -53,6 +53,7 @@ const external_node_url_namespaceObject = require("node:url");
53
53
  const external_create_package_root_cjs_namespaceObject = require("../create-package-root.cjs");
54
54
  const external_ultramodern_package_source_cjs_namespaceObject = require("../ultramodern-package-source.cjs");
55
55
  const mf_validation_cjs_namespaceObject = require("../ultramodern-workspace/mf-validation.cjs");
56
+ const versions_cjs_namespaceObject = require("../ultramodern-workspace/versions.cjs");
56
57
  const workspace_scripts_cjs_namespaceObject = require("../ultramodern-workspace/workspace-scripts.cjs");
57
58
  const external_config_cjs_namespaceObject = require("./config.cjs");
58
59
  const commands_dirname = external_node_path_default().dirname((0, external_node_url_namespaceObject.fileURLToPath)(__rslib_import_meta_url__));
@@ -198,6 +199,58 @@ function updateModernDependencies(packageJson, packageSource) {
198
199
  }
199
200
  return changed;
200
201
  }
202
+ const generatedToolingDependencyPins = new Map([
203
+ [
204
+ '@effect/tsgo',
205
+ versions_cjs_namespaceObject.EFFECT_TSGO_VERSION
206
+ ],
207
+ [
208
+ "@typescript/native-preview",
209
+ versions_cjs_namespaceObject.TYPESCRIPT_NATIVE_PREVIEW_VERSION
210
+ ],
211
+ [
212
+ 'oxfmt',
213
+ versions_cjs_namespaceObject.OXFMT_VERSION
214
+ ],
215
+ [
216
+ 'oxlint',
217
+ versions_cjs_namespaceObject.OXLINT_VERSION
218
+ ],
219
+ [
220
+ 'wrangler',
221
+ versions_cjs_namespaceObject.WRANGLER_VERSION
222
+ ],
223
+ [
224
+ 'zephyr-agent',
225
+ versions_cjs_namespaceObject.ZEPHYR_AGENT_VERSION
226
+ ],
227
+ [
228
+ 'zephyr-rspack-plugin',
229
+ versions_cjs_namespaceObject.ZEPHYR_RSPACK_PLUGIN_VERSION
230
+ ]
231
+ ]);
232
+ const strictEffectTrustPolicyExclusions = [
233
+ `effect@${versions_cjs_namespaceObject.EFFECT_VERSION}`,
234
+ `@effect/opentelemetry@${versions_cjs_namespaceObject.EFFECT_VERSION}`
235
+ ];
236
+ function updateGeneratedToolingDependencies(packageJson) {
237
+ let changed = false;
238
+ for (const section of [
239
+ 'dependencies',
240
+ 'devDependencies',
241
+ 'peerDependencies',
242
+ 'optionalDependencies'
243
+ ]){
244
+ const dependencies = packageJson[section];
245
+ if (!(!dependencies || 'object' != typeof dependencies || Array.isArray(dependencies))) {
246
+ for (const [packageName, version] of generatedToolingDependencyPins)if (Object.prototype.hasOwnProperty.call(dependencies, packageName) && dependencies[packageName] !== version) {
247
+ dependencies[packageName] = version;
248
+ changed = true;
249
+ }
250
+ }
251
+ }
252
+ return changed;
253
+ }
201
254
  function normalizeStrictEffectApiMetadata(value) {
202
255
  const api = value.api;
203
256
  if (!api || 'object' != typeof api || Array.isArray(api)) return false;
@@ -263,6 +316,74 @@ function normalizeStrictEffectApiMetadata(value) {
263
316
  }
264
317
  return changed;
265
318
  }
319
+ function replaceYamlLine(source, pattern, replacement) {
320
+ const updated = source.replace(pattern, replacement);
321
+ return {
322
+ source: updated,
323
+ changed: updated !== source
324
+ };
325
+ }
326
+ function ensureYamlListItem(source, key, item) {
327
+ const itemLine = ` - '${item}'`;
328
+ const headerPattern = new RegExp(`^${key}:\\n(?:(?: - .+\\n)*)`, 'mu');
329
+ const existingItemPattern = new RegExp(`^ - '${item.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&')}'$`, 'mu');
330
+ if (existingItemPattern.test(source)) return {
331
+ source,
332
+ changed: false
333
+ };
334
+ const header = source.match(headerPattern);
335
+ if (header) return {
336
+ source: source.replace(headerPattern, `${header[0]}${itemLine}\n`),
337
+ changed: true
338
+ };
339
+ const block = `${key}:\n${itemLine}\n`;
340
+ const afterTrustPolicyIgnore = source.replace(/^(trustPolicyIgnoreAfter: .+\n)/mu, `$1${block}`);
341
+ if (afterTrustPolicyIgnore !== source) return {
342
+ source: afterTrustPolicyIgnore,
343
+ changed: true
344
+ };
345
+ return {
346
+ source: `${source.trimEnd()}\n${block}`,
347
+ changed: true
348
+ };
349
+ }
350
+ function updateGeneratedPnpmWorkspacePolicy(workspaceRoot) {
351
+ const workspaceFile = external_node_path_default().join(workspaceRoot, 'pnpm-workspace.yaml');
352
+ if (!external_node_fs_default().existsSync(workspaceFile)) return false;
353
+ let source = external_node_fs_default().readFileSync(workspaceFile, 'utf-8');
354
+ let changed = false;
355
+ const replacements = [
356
+ [
357
+ /^ {4}'@effect\/vitest>effect': .+$/mu,
358
+ ` '@effect/vitest>effect': '${versions_cjs_namespaceObject.EFFECT_VERSION}'`
359
+ ],
360
+ [
361
+ /^ {2}'@effect\/vitest': .+$/mu,
362
+ ` '@effect/vitest': ${versions_cjs_namespaceObject.EFFECT_VITEST_VERSION}`
363
+ ],
364
+ [
365
+ /^ {2}effect: .+$/mu,
366
+ ` effect: ${versions_cjs_namespaceObject.EFFECT_VERSION}`
367
+ ]
368
+ ];
369
+ for (const [pattern, replacement] of replacements){
370
+ const result = replaceYamlLine(source, pattern, replacement);
371
+ source = result.source;
372
+ changed = result.changed || changed;
373
+ }
374
+ for (const item of strictEffectTrustPolicyExclusions){
375
+ const packageName = item.slice(0, item.lastIndexOf('@'));
376
+ const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
377
+ const currentVersion = replaceYamlLine(source, new RegExp(`^ {2}- '${escapedPackageName}@[^']+'$`, 'mu'), ` - '${item}'`);
378
+ source = currentVersion.source;
379
+ changed = currentVersion.changed || changed;
380
+ const trustPolicyExclude = ensureYamlListItem(source, 'trustPolicyExclude', item);
381
+ source = trustPolicyExclude.source;
382
+ changed = trustPolicyExclude.changed || changed;
383
+ }
384
+ if (changed) external_node_fs_default().writeFileSync(workspaceFile, source, 'utf-8');
385
+ return changed;
386
+ }
266
387
  function updateUltramodernConfig(workspaceRoot, packageSource) {
267
388
  const configPath = external_node_path_default().join(workspaceRoot, '.modernjs/ultramodern.json');
268
389
  const config = readJsonFile(configPath);
@@ -344,8 +465,9 @@ function runMigrateStrictEffect(args, context) {
344
465
  modern-js-create ultramodern migrate-strict-effect --version <version> [--skip-install]
345
466
 
346
467
  Updates generated UltraModern package-source metadata, Modern package aliases,
347
- direct Effect API topology metadata, and the pnpm lockfile for strict Effect
348
- workspaces. Source code still has to pass pnpm api:check and pnpm contract:check.
468
+ framework-owned toolchain pins, direct Effect API topology metadata, strict
469
+ Effect pnpm overrides/trust policy, and the pnpm lockfile. Source code still
470
+ has to pass pnpm api:check and pnpm contract:check.
349
471
  `);
350
472
  return 0;
351
473
  }
@@ -363,9 +485,13 @@ workspaces. Source code still has to pass pnpm api:check and pnpm contract:check
363
485
  config: './.modernjs/ultramodern.json'
364
486
  };
365
487
  }
366
- if (updateModernDependencies(packageJson, packageSource)) writeJsonFile(packageFile, packageJson);
488
+ const modernDependenciesChanged = updateModernDependencies(packageJson, packageSource);
489
+ const toolingDependenciesChanged = updateGeneratedToolingDependencies(packageJson);
490
+ const changed = modernDependenciesChanged || toolingDependenciesChanged;
491
+ if (changed) writeJsonFile(packageFile, packageJson);
367
492
  else if ('package.json' === relativePackageFile) writeJsonFile(packageFile, packageJson);
368
493
  }
494
+ updateGeneratedPnpmWorkspacePolicy(context.workspaceRoot);
369
495
  if (!hasFlag(args, '--skip-install')) {
370
496
  const status = runPnpmLockfileRefresh(context);
371
497
  if (0 !== status) return status;
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { resolveCreatePackageRoot } from "../create-package-root.js";
7
7
  import { BLEEDINGDEV_PACKAGE_NAME_PREFIX, BLEEDINGDEV_PACKAGE_SCOPE, ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, ULTRAMODERN_WORKSPACE_MODERN_PACKAGES, WORKSPACE_PACKAGE_VERSION, modernPackageSpecifier } from "../ultramodern-package-source.js";
8
8
  import { validateModuleFederationTypes } from "../ultramodern-workspace/mf-validation.js";
9
+ import { EFFECT_TSGO_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, OXFMT_VERSION, OXLINT_VERSION, TYPESCRIPT_NATIVE_PREVIEW_VERSION, WRANGLER_VERSION, ZEPHYR_AGENT_VERSION, ZEPHYR_RSPACK_PLUGIN_VERSION } from "../ultramodern-workspace/versions.js";
9
10
  import { createWorkspaceValidationScript } from "../ultramodern-workspace/workspace-scripts.js";
10
11
  import { readUltramodernConfig, workspaceAppsFromToolingConfig } from "./config.js";
11
12
  const commands_dirname = node_path.dirname(fileURLToPath(import.meta.url));
@@ -151,6 +152,58 @@ function updateModernDependencies(packageJson, packageSource) {
151
152
  }
152
153
  return changed;
153
154
  }
155
+ const generatedToolingDependencyPins = new Map([
156
+ [
157
+ '@effect/tsgo',
158
+ EFFECT_TSGO_VERSION
159
+ ],
160
+ [
161
+ "@typescript/native-preview",
162
+ TYPESCRIPT_NATIVE_PREVIEW_VERSION
163
+ ],
164
+ [
165
+ 'oxfmt',
166
+ OXFMT_VERSION
167
+ ],
168
+ [
169
+ 'oxlint',
170
+ OXLINT_VERSION
171
+ ],
172
+ [
173
+ 'wrangler',
174
+ WRANGLER_VERSION
175
+ ],
176
+ [
177
+ 'zephyr-agent',
178
+ ZEPHYR_AGENT_VERSION
179
+ ],
180
+ [
181
+ 'zephyr-rspack-plugin',
182
+ ZEPHYR_RSPACK_PLUGIN_VERSION
183
+ ]
184
+ ]);
185
+ const strictEffectTrustPolicyExclusions = [
186
+ `effect@${EFFECT_VERSION}`,
187
+ `@effect/opentelemetry@${EFFECT_VERSION}`
188
+ ];
189
+ function updateGeneratedToolingDependencies(packageJson) {
190
+ let changed = false;
191
+ for (const section of [
192
+ 'dependencies',
193
+ 'devDependencies',
194
+ 'peerDependencies',
195
+ 'optionalDependencies'
196
+ ]){
197
+ const dependencies = packageJson[section];
198
+ if (!(!dependencies || 'object' != typeof dependencies || Array.isArray(dependencies))) {
199
+ for (const [packageName, version] of generatedToolingDependencyPins)if (Object.prototype.hasOwnProperty.call(dependencies, packageName) && dependencies[packageName] !== version) {
200
+ dependencies[packageName] = version;
201
+ changed = true;
202
+ }
203
+ }
204
+ }
205
+ return changed;
206
+ }
154
207
  function normalizeStrictEffectApiMetadata(value) {
155
208
  const api = value.api;
156
209
  if (!api || 'object' != typeof api || Array.isArray(api)) return false;
@@ -216,6 +269,74 @@ function normalizeStrictEffectApiMetadata(value) {
216
269
  }
217
270
  return changed;
218
271
  }
272
+ function replaceYamlLine(source, pattern, replacement) {
273
+ const updated = source.replace(pattern, replacement);
274
+ return {
275
+ source: updated,
276
+ changed: updated !== source
277
+ };
278
+ }
279
+ function ensureYamlListItem(source, key, item) {
280
+ const itemLine = ` - '${item}'`;
281
+ const headerPattern = new RegExp(`^${key}:\\n(?:(?: - .+\\n)*)`, 'mu');
282
+ const existingItemPattern = new RegExp(`^ - '${item.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&')}'$`, 'mu');
283
+ if (existingItemPattern.test(source)) return {
284
+ source,
285
+ changed: false
286
+ };
287
+ const header = source.match(headerPattern);
288
+ if (header) return {
289
+ source: source.replace(headerPattern, `${header[0]}${itemLine}\n`),
290
+ changed: true
291
+ };
292
+ const block = `${key}:\n${itemLine}\n`;
293
+ const afterTrustPolicyIgnore = source.replace(/^(trustPolicyIgnoreAfter: .+\n)/mu, `$1${block}`);
294
+ if (afterTrustPolicyIgnore !== source) return {
295
+ source: afterTrustPolicyIgnore,
296
+ changed: true
297
+ };
298
+ return {
299
+ source: `${source.trimEnd()}\n${block}`,
300
+ changed: true
301
+ };
302
+ }
303
+ function updateGeneratedPnpmWorkspacePolicy(workspaceRoot) {
304
+ const workspaceFile = node_path.join(workspaceRoot, 'pnpm-workspace.yaml');
305
+ if (!node_fs.existsSync(workspaceFile)) return false;
306
+ let source = node_fs.readFileSync(workspaceFile, 'utf-8');
307
+ let changed = false;
308
+ const replacements = [
309
+ [
310
+ /^ {4}'@effect\/vitest>effect': .+$/mu,
311
+ ` '@effect/vitest>effect': '${EFFECT_VERSION}'`
312
+ ],
313
+ [
314
+ /^ {2}'@effect\/vitest': .+$/mu,
315
+ ` '@effect/vitest': ${EFFECT_VITEST_VERSION}`
316
+ ],
317
+ [
318
+ /^ {2}effect: .+$/mu,
319
+ ` effect: ${EFFECT_VERSION}`
320
+ ]
321
+ ];
322
+ for (const [pattern, replacement] of replacements){
323
+ const result = replaceYamlLine(source, pattern, replacement);
324
+ source = result.source;
325
+ changed = result.changed || changed;
326
+ }
327
+ for (const item of strictEffectTrustPolicyExclusions){
328
+ const packageName = item.slice(0, item.lastIndexOf('@'));
329
+ const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
330
+ const currentVersion = replaceYamlLine(source, new RegExp(`^ {2}- '${escapedPackageName}@[^']+'$`, 'mu'), ` - '${item}'`);
331
+ source = currentVersion.source;
332
+ changed = currentVersion.changed || changed;
333
+ const trustPolicyExclude = ensureYamlListItem(source, 'trustPolicyExclude', item);
334
+ source = trustPolicyExclude.source;
335
+ changed = trustPolicyExclude.changed || changed;
336
+ }
337
+ if (changed) node_fs.writeFileSync(workspaceFile, source, 'utf-8');
338
+ return changed;
339
+ }
219
340
  function updateUltramodernConfig(workspaceRoot, packageSource) {
220
341
  const configPath = node_path.join(workspaceRoot, '.modernjs/ultramodern.json');
221
342
  const config = readJsonFile(configPath);
@@ -297,8 +418,9 @@ function runMigrateStrictEffect(args, context) {
297
418
  modern-js-create ultramodern migrate-strict-effect --version <version> [--skip-install]
298
419
 
299
420
  Updates generated UltraModern package-source metadata, Modern package aliases,
300
- direct Effect API topology metadata, and the pnpm lockfile for strict Effect
301
- workspaces. Source code still has to pass pnpm api:check and pnpm contract:check.
421
+ framework-owned toolchain pins, direct Effect API topology metadata, strict
422
+ Effect pnpm overrides/trust policy, and the pnpm lockfile. Source code still
423
+ has to pass pnpm api:check and pnpm contract:check.
302
424
  `);
303
425
  return 0;
304
426
  }
@@ -316,9 +438,13 @@ workspaces. Source code still has to pass pnpm api:check and pnpm contract:check
316
438
  config: './.modernjs/ultramodern.json'
317
439
  };
318
440
  }
319
- if (updateModernDependencies(packageJson, packageSource)) writeJsonFile(packageFile, packageJson);
441
+ const modernDependenciesChanged = updateModernDependencies(packageJson, packageSource);
442
+ const toolingDependenciesChanged = updateGeneratedToolingDependencies(packageJson);
443
+ const changed = modernDependenciesChanged || toolingDependenciesChanged;
444
+ if (changed) writeJsonFile(packageFile, packageJson);
320
445
  else if ('package.json' === relativePackageFile) writeJsonFile(packageFile, packageJson);
321
446
  }
447
+ updateGeneratedPnpmWorkspacePolicy(context.workspaceRoot);
322
448
  if (!hasFlag(args, '--skip-install')) {
323
449
  const status = runPnpmLockfileRefresh(context);
324
450
  if (0 !== status) return status;
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
7
7
  import { resolveCreatePackageRoot } from "../create-package-root.js";
8
8
  import { BLEEDINGDEV_PACKAGE_NAME_PREFIX, BLEEDINGDEV_PACKAGE_SCOPE, ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, ULTRAMODERN_WORKSPACE_MODERN_PACKAGES, WORKSPACE_PACKAGE_VERSION, modernPackageSpecifier } from "../ultramodern-package-source.js";
9
9
  import { validateModuleFederationTypes } from "../ultramodern-workspace/mf-validation.js";
10
+ import { EFFECT_TSGO_VERSION, EFFECT_VERSION, EFFECT_VITEST_VERSION, OXFMT_VERSION, OXLINT_VERSION, TYPESCRIPT_NATIVE_PREVIEW_VERSION, WRANGLER_VERSION, ZEPHYR_AGENT_VERSION, ZEPHYR_RSPACK_PLUGIN_VERSION } from "../ultramodern-workspace/versions.js";
10
11
  import { createWorkspaceValidationScript } from "../ultramodern-workspace/workspace-scripts.js";
11
12
  import { readUltramodernConfig, workspaceAppsFromToolingConfig } from "./config.js";
12
13
  const commands_dirname = node_path.dirname(fileURLToPath(import.meta.url));
@@ -152,6 +153,58 @@ function updateModernDependencies(packageJson, packageSource) {
152
153
  }
153
154
  return changed;
154
155
  }
156
+ const generatedToolingDependencyPins = new Map([
157
+ [
158
+ '@effect/tsgo',
159
+ EFFECT_TSGO_VERSION
160
+ ],
161
+ [
162
+ "@typescript/native-preview",
163
+ TYPESCRIPT_NATIVE_PREVIEW_VERSION
164
+ ],
165
+ [
166
+ 'oxfmt',
167
+ OXFMT_VERSION
168
+ ],
169
+ [
170
+ 'oxlint',
171
+ OXLINT_VERSION
172
+ ],
173
+ [
174
+ 'wrangler',
175
+ WRANGLER_VERSION
176
+ ],
177
+ [
178
+ 'zephyr-agent',
179
+ ZEPHYR_AGENT_VERSION
180
+ ],
181
+ [
182
+ 'zephyr-rspack-plugin',
183
+ ZEPHYR_RSPACK_PLUGIN_VERSION
184
+ ]
185
+ ]);
186
+ const strictEffectTrustPolicyExclusions = [
187
+ `effect@${EFFECT_VERSION}`,
188
+ `@effect/opentelemetry@${EFFECT_VERSION}`
189
+ ];
190
+ function updateGeneratedToolingDependencies(packageJson) {
191
+ let changed = false;
192
+ for (const section of [
193
+ 'dependencies',
194
+ 'devDependencies',
195
+ 'peerDependencies',
196
+ 'optionalDependencies'
197
+ ]){
198
+ const dependencies = packageJson[section];
199
+ if (!(!dependencies || 'object' != typeof dependencies || Array.isArray(dependencies))) {
200
+ for (const [packageName, version] of generatedToolingDependencyPins)if (Object.prototype.hasOwnProperty.call(dependencies, packageName) && dependencies[packageName] !== version) {
201
+ dependencies[packageName] = version;
202
+ changed = true;
203
+ }
204
+ }
205
+ }
206
+ return changed;
207
+ }
155
208
  function normalizeStrictEffectApiMetadata(value) {
156
209
  const api = value.api;
157
210
  if (!api || 'object' != typeof api || Array.isArray(api)) return false;
@@ -217,6 +270,74 @@ function normalizeStrictEffectApiMetadata(value) {
217
270
  }
218
271
  return changed;
219
272
  }
273
+ function replaceYamlLine(source, pattern, replacement) {
274
+ const updated = source.replace(pattern, replacement);
275
+ return {
276
+ source: updated,
277
+ changed: updated !== source
278
+ };
279
+ }
280
+ function ensureYamlListItem(source, key, item) {
281
+ const itemLine = ` - '${item}'`;
282
+ const headerPattern = new RegExp(`^${key}:\\n(?:(?: - .+\\n)*)`, 'mu');
283
+ const existingItemPattern = new RegExp(`^ - '${item.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&')}'$`, 'mu');
284
+ if (existingItemPattern.test(source)) return {
285
+ source,
286
+ changed: false
287
+ };
288
+ const header = source.match(headerPattern);
289
+ if (header) return {
290
+ source: source.replace(headerPattern, `${header[0]}${itemLine}\n`),
291
+ changed: true
292
+ };
293
+ const block = `${key}:\n${itemLine}\n`;
294
+ const afterTrustPolicyIgnore = source.replace(/^(trustPolicyIgnoreAfter: .+\n)/mu, `$1${block}`);
295
+ if (afterTrustPolicyIgnore !== source) return {
296
+ source: afterTrustPolicyIgnore,
297
+ changed: true
298
+ };
299
+ return {
300
+ source: `${source.trimEnd()}\n${block}`,
301
+ changed: true
302
+ };
303
+ }
304
+ function updateGeneratedPnpmWorkspacePolicy(workspaceRoot) {
305
+ const workspaceFile = node_path.join(workspaceRoot, 'pnpm-workspace.yaml');
306
+ if (!node_fs.existsSync(workspaceFile)) return false;
307
+ let source = node_fs.readFileSync(workspaceFile, 'utf-8');
308
+ let changed = false;
309
+ const replacements = [
310
+ [
311
+ /^ {4}'@effect\/vitest>effect': .+$/mu,
312
+ ` '@effect/vitest>effect': '${EFFECT_VERSION}'`
313
+ ],
314
+ [
315
+ /^ {2}'@effect\/vitest': .+$/mu,
316
+ ` '@effect/vitest': ${EFFECT_VITEST_VERSION}`
317
+ ],
318
+ [
319
+ /^ {2}effect: .+$/mu,
320
+ ` effect: ${EFFECT_VERSION}`
321
+ ]
322
+ ];
323
+ for (const [pattern, replacement] of replacements){
324
+ const result = replaceYamlLine(source, pattern, replacement);
325
+ source = result.source;
326
+ changed = result.changed || changed;
327
+ }
328
+ for (const item of strictEffectTrustPolicyExclusions){
329
+ const packageName = item.slice(0, item.lastIndexOf('@'));
330
+ const escapedPackageName = packageName.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
331
+ const currentVersion = replaceYamlLine(source, new RegExp(`^ {2}- '${escapedPackageName}@[^']+'$`, 'mu'), ` - '${item}'`);
332
+ source = currentVersion.source;
333
+ changed = currentVersion.changed || changed;
334
+ const trustPolicyExclude = ensureYamlListItem(source, 'trustPolicyExclude', item);
335
+ source = trustPolicyExclude.source;
336
+ changed = trustPolicyExclude.changed || changed;
337
+ }
338
+ if (changed) node_fs.writeFileSync(workspaceFile, source, 'utf-8');
339
+ return changed;
340
+ }
220
341
  function updateUltramodernConfig(workspaceRoot, packageSource) {
221
342
  const configPath = node_path.join(workspaceRoot, '.modernjs/ultramodern.json');
222
343
  const config = readJsonFile(configPath);
@@ -298,8 +419,9 @@ function runMigrateStrictEffect(args, context) {
298
419
  modern-js-create ultramodern migrate-strict-effect --version <version> [--skip-install]
299
420
 
300
421
  Updates generated UltraModern package-source metadata, Modern package aliases,
301
- direct Effect API topology metadata, and the pnpm lockfile for strict Effect
302
- workspaces. Source code still has to pass pnpm api:check and pnpm contract:check.
422
+ framework-owned toolchain pins, direct Effect API topology metadata, strict
423
+ Effect pnpm overrides/trust policy, and the pnpm lockfile. Source code still
424
+ has to pass pnpm api:check and pnpm contract:check.
303
425
  `);
304
426
  return 0;
305
427
  }
@@ -317,9 +439,13 @@ workspaces. Source code still has to pass pnpm api:check and pnpm contract:check
317
439
  config: './.modernjs/ultramodern.json'
318
440
  };
319
441
  }
320
- if (updateModernDependencies(packageJson, packageSource)) writeJsonFile(packageFile, packageJson);
442
+ const modernDependenciesChanged = updateModernDependencies(packageJson, packageSource);
443
+ const toolingDependenciesChanged = updateGeneratedToolingDependencies(packageJson);
444
+ const changed = modernDependenciesChanged || toolingDependenciesChanged;
445
+ if (changed) writeJsonFile(packageFile, packageJson);
321
446
  else if ('package.json' === relativePackageFile) writeJsonFile(packageFile, packageJson);
322
447
  }
448
+ updateGeneratedPnpmWorkspacePolicy(context.workspaceRoot);
323
449
  if (!hasFlag(args, '--skip-install')) {
324
450
  const status = runPnpmLockfileRefresh(context);
325
451
  if (0 !== status) return status;
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.5.0-ultramodern.2",
24
+ "version": "3.5.0-ultramodern.4",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/esm-node/index.js",
27
27
  "bin": {
@@ -77,7 +77,7 @@
77
77
  "@modern-js/codesmith": "2.6.9",
78
78
  "oxfmt": "0.56.0",
79
79
  "ultracite": "7.8.3",
80
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.5.0-ultramodern.2"
80
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.5.0-ultramodern.4"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@rslib/core": "0.23.1",
@@ -99,6 +99,6 @@
99
99
  "test": "rm -rf dist && rslib build -c rslibconfig.mts && rstest --passWithNoTests"
100
100
  },
101
101
  "ultramodern": {
102
- "frameworkVersion": "3.5.0-ultramodern.2"
102
+ "frameworkVersion": "3.5.0-ultramodern.4"
103
103
  }
104
104
  }
@@ -169,19 +169,23 @@ in API modules.
169
169
  Generated pnpm overrides pin the framework-compatible Effect cohort. Keep
170
170
  `effect` and `@effect/vitest` aligned with `pnpm-workspace.yaml`; do not add
171
171
  new direct package-level Effect versions unless the whole UltraModern cohort is
172
- upgraded.
172
+ upgraded. The generated pnpm trust policy intentionally excludes the matching
173
+ `effect` and `@effect/opentelemetry` cohort packages from no-downgrade checks
174
+ while Effect's beta publishes move from trusted-publisher metadata to
175
+ provenance attestations.
173
176
 
174
177
  For older generated workspaces, run the framework migration command first:
175
178
 
176
179
  ```bash
177
- pnpm dlx @bleedingdev/modern-js-create@3.5.0-ultramodern.2 ultramodern \
178
- migrate-strict-effect --version 3.5.0-ultramodern.2
180
+ pnpm dlx @bleedingdev/modern-js-create@3.5.0-ultramodern.4 ultramodern \
181
+ migrate-strict-effect --version 3.5.0-ultramodern.4
179
182
  pnpm api:check
180
183
  pnpm contract:check
181
184
  ```
182
185
 
183
186
  The command updates generated package-source metadata, Modern package aliases,
184
- direct API topology metadata, and the lockfile. Remaining failures are source
187
+ framework-owned toolchain pins, direct API topology metadata, strict Effect pnpm
188
+ overrides/trust policy, and the lockfile. Remaining failures are source
185
189
  migration work; fix the owning API files instead of adding compatibility shims.
186
190
 
187
191
  ## Troubleshooting
@@ -24,6 +24,9 @@ minimumReleaseAgeExclude:
24
24
  - 'ts-checker-rspack-plugin'
25
25
  trustPolicy: no-downgrade
26
26
  trustPolicyIgnoreAfter: 1440
27
+ trustPolicyExclude:
28
+ - 'effect@{{effectVersion}}'
29
+ - '@effect/opentelemetry@{{effectVersion}}'
27
30
  blockExoticSubdeps: true
28
31
  engineStrict: true
29
32
  pmOnFail: error