@volter/twin-standard 1.0.155 → 1.0.156

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.
@@ -3,7 +3,7 @@ import { createHash } from 'node:crypto';
3
3
  import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
5
  import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
6
- import { spawn } from 'node:child_process';
6
+ import { spawn, spawnSync } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import { standardPackageFile } from "./package-file.js";
9
9
  import { dependencyLock } from "./dependency-lock.js";
@@ -91,7 +91,7 @@ export function prepareConsumerApp(packDir, appDir, installation) {
91
91
  throw new Error(`${name}: customer SDK dependencies require exact registry versions`);
92
92
  }
93
93
  const files = Object.entries(recipe.files).map(([path, content]) => {
94
- if (isAbsolute(path) || path.includes('\\') || path.split('/').some((part) => !part || part === '.' || part === '..') || path.split('/').includes('node_modules') || ['.env', '.npmrc', '.volter/world.json'].includes(path))
94
+ if (isAbsolute(path) || path.includes('\\') || path.split('/').some((part) => !part || part === '.' || part === '..' || part === '.git') || path.split('/').includes('node_modules') || ['.env', '.npmrc', '.volter/world.json'].includes(path))
95
95
  throw new Error(`Invalid consumer file: ${path}`);
96
96
  if (typeof content !== 'string')
97
97
  throw new Error('Consumer files must contain text');
@@ -113,6 +113,17 @@ export function prepareConsumerApp(packDir, appDir, installation) {
113
113
  mkdirSync(dirname(join(appDir, path)), { recursive: true });
114
114
  writeFileSync(join(appDir, path), content);
115
115
  }
116
+ // World init reads committed example values. The recipe is this new application's source.
117
+ const gitEnv = Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.startsWith('GIT_')));
118
+ for (const args of [
119
+ ['init', '--quiet', '--template='],
120
+ ['add', '--force', '--', ...files.map(([path]) => path)],
121
+ ['-c', 'user.name=World consumer', '-c', 'user.email=consumer@world.test', '-c', 'commit.gpgsign=false', '-c', `core.hooksPath=${join(appDir, '.git/hooks')}`, 'commit', '--quiet', '-m', 'Materialize customer application'],
122
+ ]) {
123
+ const result = spawnSync('git', ['-C', appDir, ...args], { env: gitEnv, encoding: 'utf8' });
124
+ if (result.error || result.status !== 0)
125
+ throw new Error(`Consumer source ${args[0]} failed: ${result.error?.message ?? result.stderr.trim()}`);
126
+ }
116
127
  return { recipeSha256: createHash('sha256').update(JSON.stringify(recipe)).digest('hex'), dependencies: app.dependencies ?? {} };
117
128
  }
118
129
  export async function runConsumerWorkflow(packDir, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/twin-standard",
3
- "version": "1.0.155",
3
+ "version": "1.0.156",
4
4
  "description": "The Protocol 3 standard for Volter twin packs: the criteria a pack is graded by, the grader, the gate a pack repository and the twin catalog run on every change and submission, and the spec derivation the grade checks against. Pin a version: every place a pack is judged judges by that standard.",
5
5
  "keywords": [
6
6
  "twin",
package/src/consumer.ts CHANGED
@@ -3,7 +3,7 @@ import { createHash } from 'node:crypto';
3
3
  import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
5
  import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
6
- import { spawn } from 'node:child_process';
6
+ import { spawn, spawnSync } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import { standardPackageFile } from './package-file.ts';
9
9
  import { dependencyLock } from './dependency-lock.ts';
@@ -87,7 +87,7 @@ export function prepareConsumerApp(packDir: string, appDir: string, installation
87
87
  if (typeof version !== 'string' || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) throw new Error(`${name}: customer SDK dependencies require exact registry versions`);
88
88
  }
89
89
  const files = Object.entries(recipe.files).map(([path, content]) => {
90
- if (isAbsolute(path) || path.includes('\\') || path.split('/').some((part) => !part || part === '.' || part === '..') || path.split('/').includes('node_modules') || ['.env', '.npmrc', '.volter/world.json'].includes(path)) throw new Error(`Invalid consumer file: ${path}`);
90
+ if (isAbsolute(path) || path.includes('\\') || path.split('/').some((part) => !part || part === '.' || part === '..' || part === '.git') || path.split('/').includes('node_modules') || ['.env', '.npmrc', '.volter/world.json'].includes(path)) throw new Error(`Invalid consumer file: ${path}`);
91
91
  if (typeof content !== 'string') throw new Error('Consumer files must contain text');
92
92
  return [path, content] as [string, string];
93
93
  });
@@ -105,6 +105,16 @@ export function prepareConsumerApp(packDir: string, appDir: string, installation
105
105
  mkdirSync(dirname(join(appDir, path)), { recursive: true });
106
106
  writeFileSync(join(appDir, path), content);
107
107
  }
108
+ // World init reads committed example values. The recipe is this new application's source.
109
+ const gitEnv = Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.startsWith('GIT_')));
110
+ for (const args of [
111
+ ['init', '--quiet', '--template='],
112
+ ['add', '--force', '--', ...files.map(([path]) => path)],
113
+ ['-c', 'user.name=World consumer', '-c', 'user.email=consumer@world.test', '-c', 'commit.gpgsign=false', '-c', `core.hooksPath=${join(appDir, '.git/hooks')}`, 'commit', '--quiet', '-m', 'Materialize customer application'],
114
+ ]) {
115
+ const result = spawnSync('git', ['-C', appDir, ...args], { env: gitEnv, encoding: 'utf8' });
116
+ if (result.error || result.status !== 0) throw new Error(`Consumer source ${args[0]} failed: ${result.error?.message ?? result.stderr.trim()}`);
117
+ }
108
118
  return { recipeSha256: createHash('sha256').update(JSON.stringify(recipe)).digest('hex'), dependencies: app.dependencies ?? {} };
109
119
  }
110
120