@teurons/vds 0.0.3 → 0.0.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.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/dist/vds.js +125 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -34,6 +34,7 @@ Set up VDS in this project.
34
34
  npx @teurons/vds init [folder] # start a workspace (never overwrites one)
35
35
  npx @teurons/vds check [folder] # print every problem; exit 1 if any
36
36
  npx @teurons/vds index [folder] # print the index: every flow and rule, one line each
37
+ npx @teurons/vds update [folder] # install the newest vds, refresh VDS.md
37
38
  ```
38
39
 
39
40
  `folder` defaults to the current folder.
package/dist/vds.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/bin.js
4
- import { readdirSync, readFileSync as readFileSync2 } from "node:fs";
5
- import { join as join2, resolve } from "node:path";
4
+ import { readdirSync, readFileSync as readFileSync4 } from "node:fs";
5
+ import { join as join3, resolve } from "node:path";
6
6
 
7
7
  // ../../specs/js/load.js
8
8
  import { readFileSync } from "node:fs";
@@ -97,8 +97,18 @@ function loadMapper() {
97
97
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
98
98
  import { createServer } from "node:net";
99
99
  import { join } from "node:path";
100
- var FOLDERS = ["capabilities", "flows", "scenarios"];
101
- var FIRST_PORT = 4600;
100
+
101
+ // src/version.js
102
+ import { readFileSync as readFileSync2 } from "node:fs";
103
+ var baked = false ? null : "0.0.4";
104
+ var VERSION = process.env.VDS_VERSION ?? baked ?? JSON.parse(readFileSync2(new URL("../package.json", import.meta.url), "utf8")).version;
105
+ function compare(a, b) {
106
+ const [x, y] = [a, b].map((v) => v.split("-")[0].split(".").map(Number));
107
+ for (let i = 0; i < 3; i++) if ((x[i] || 0) !== (y[i] || 0)) return (x[i] || 0) - (y[i] || 0);
108
+ return 0;
109
+ }
110
+
111
+ // src/guide.js
102
112
  var GUIDE = `# Working in this VDS workspace
103
113
 
104
114
  This folder holds the product's requirements, flows and scenarios as plain files.
@@ -118,6 +128,15 @@ Read and write them with the vds command.
118
128
  4. Point flows at requirement ids. A state lists the ids it needs under \`requirements:\`.
119
129
  5. Run \`vds check\` before you finish. It prints "valid" when nothing is wrong; fix every problem it lists.
120
130
  `;
131
+ var markerFor = (version) => `<!-- vds ${version} -->`;
132
+ function guideText() {
133
+ return `${markerFor(VERSION)}
134
+ ${GUIDE}`;
135
+ }
136
+
137
+ // src/init.js
138
+ var FOLDERS = ["capabilities", "flows", "scenarios"];
139
+ var FIRST_PORT = 4600;
121
140
  async function freePort() {
122
141
  for (let port = FIRST_PORT; ; port++) {
123
142
  const free = await new Promise((resolve2) => {
@@ -136,12 +155,98 @@ async function init(root) {
136
155
  }
137
156
  const port = await freePort();
138
157
  for (const dir of FOLDERS) mkdirSync(join(root, dir), { recursive: true });
139
- writeFileSync(join(root, "VDS.md"), GUIDE);
158
+ writeFileSync(join(root, "VDS.md"), guideText());
140
159
  writeFileSync(join(root, ".vds.json"), `${JSON.stringify({ port })}
141
160
  `);
142
161
  return 0;
143
162
  }
144
163
 
164
+ // src/update.js
165
+ import { spawnSync } from "node:child_process";
166
+ import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
167
+ import { dirname, join as join2 } from "node:path";
168
+ import { fileURLToPath } from "node:url";
169
+ var REGISTRY = "https://registry.npmjs.org";
170
+ var MARKER = /^<!-- vds (\d+\.\d+\.\d+\S*) -->/;
171
+ async function newestVersion() {
172
+ const base = process.env.VDS_REGISTRY ?? REGISTRY;
173
+ try {
174
+ const res = await fetch(`${base}/@teurons%2fvds`);
175
+ if (!res.ok) throw new Error(`status ${res.status}`);
176
+ return (await res.json())["dist-tags"].latest;
177
+ } catch (e) {
178
+ throw new Error(`could not reach the npm registry: ${e.message}`);
179
+ }
180
+ }
181
+ function installKind() {
182
+ const file = fileURLToPath(import.meta.url);
183
+ if (file.includes("/_npx/")) return { kind: "npx" };
184
+ const at = file.indexOf("/node_modules/");
185
+ const root = at > 0 ? file.slice(0, at) : null;
186
+ if (root && existsSync2(join2(root, "package.json"))) return { kind: "project", root };
187
+ return { kind: "global" };
188
+ }
189
+ function installPlan(version) {
190
+ const spec = `@teurons/vds@${version}`;
191
+ const found = installKind();
192
+ if (found.kind === "project") {
193
+ const has = (f) => existsSync2(join2(found.root, f));
194
+ const cmd = has("pnpm-lock.yaml") ? `pnpm add -D ${spec}` : has("yarn.lock") ? `yarn add -D ${spec}` : `npm install -D ${spec}`;
195
+ return { ...found, cmd, bin: join2(found.root, "node_modules", ".bin", "vds") };
196
+ }
197
+ return { ...found, cmd: `npm install -g ${spec}`, bin: "vds" };
198
+ }
199
+ function refreshGuide(folder) {
200
+ const path = join2(folder, "VDS.md");
201
+ if (!existsSync2(path)) return false;
202
+ const current = readFileSync3(path, "utf8");
203
+ const marked = current.match(MARKER)?.[1];
204
+ if (marked && compare(marked, VERSION) >= 0) return false;
205
+ const next = guideText();
206
+ if (next === current) return false;
207
+ writeFileSync2(path, next);
208
+ return true;
209
+ }
210
+ function runShell(cmd, version, cwd) {
211
+ return spawnSync(cmd, {
212
+ shell: true,
213
+ stdio: "inherit",
214
+ cwd,
215
+ env: { ...process.env, VDS_TARGET_VERSION: version }
216
+ });
217
+ }
218
+ async function update(folder, { guideOnly = false } = {}) {
219
+ if (guideOnly) {
220
+ if (refreshGuide(folder)) console.log(`VDS.md rewritten for vds ${VERSION}`);
221
+ return 0;
222
+ }
223
+ const newest = await newestVersion();
224
+ if (compare(newest, VERSION) > 0) {
225
+ const plan = installPlan(newest);
226
+ if (plan.kind === "npx" && !process.env.VDS_INSTALL_CMD) {
227
+ console.log("npx already fetches the newest vds each run; use npx @teurons/vds@latest.");
228
+ return 0;
229
+ }
230
+ const cmd = process.env.VDS_INSTALL_CMD ?? plan.cmd;
231
+ if (runShell(cmd, newest, plan.root).status !== 0) {
232
+ console.error(`install failed: ${cmd}`);
233
+ return 1;
234
+ }
235
+ console.log(`vds updated from ${VERSION} to ${newest}`);
236
+ const bin = process.env.VDS_BIN ?? plan.bin;
237
+ const r = spawnSync(bin, ["update", "--guide-only", folder], { stdio: "inherit" });
238
+ if (r.status !== 0)
239
+ console.error(`could not refresh VDS.md with vds ${newest}; run vds update again`);
240
+ return 0;
241
+ }
242
+ if (refreshGuide(folder)) {
243
+ console.log(`vds ${VERSION} is the newest; VDS.md rewritten to match it`);
244
+ return 0;
245
+ }
246
+ console.log(`everything is current: vds ${VERSION} is the newest and ${folder} is up to date`);
247
+ return 0;
248
+ }
249
+
145
250
  // src/bin.js
146
251
  var FOLDERS2 = ["capabilities", "flows", "scenarios"];
147
252
  function readWorkspace(root) {
@@ -149,16 +254,19 @@ function readWorkspace(root) {
149
254
  for (const dir of FOLDERS2) {
150
255
  let names = [];
151
256
  try {
152
- names = readdirSync(join2(root, dir));
257
+ names = readdirSync(join3(root, dir));
153
258
  } catch {
154
259
  continue;
155
260
  }
156
- for (const name of names) files[`${dir}/${name}`] = readFileSync2(join2(root, dir, name), "utf8");
261
+ for (const name of names) files[`${dir}/${name}`] = readFileSync4(join3(root, dir, name), "utf8");
157
262
  }
158
263
  return files;
159
264
  }
160
- async function main([command, folder = "."]) {
265
+ async function main(argv) {
266
+ const guideOnly = argv.includes("--guide-only");
267
+ const [command, folder = "."] = argv.filter((a) => a !== "--guide-only");
161
268
  if (command === "init") return init(resolve(folder));
269
+ if (command === "update") return update(resolve(folder), { guideOnly });
162
270
  const mapper = await loadMapper();
163
271
  const files = readWorkspace(resolve(folder));
164
272
  if (command === "check") {
@@ -176,7 +284,14 @@ async function main([command, folder = "."]) {
176
284
  process.stdout.write(mapper.v2Index(files));
177
285
  return 0;
178
286
  }
179
- console.error("usage: vds init [folder] | vds check [folder] | vds index [folder]");
287
+ console.error(
288
+ "usage: vds init [folder] | vds check [folder] | vds index [folder] | vds update [folder]"
289
+ );
180
290
  return 2;
181
291
  }
182
- process.exitCode = await main(process.argv.slice(2));
292
+ try {
293
+ process.exitCode = await main(process.argv.slice(2));
294
+ } catch (e) {
295
+ console.error(e.message);
296
+ process.exitCode = 1;
297
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teurons/vds",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "The vds command: check, index and run a VDS workspace on your machine.",
5
5
  "type": "module",
6
6
  "bin": {