@geonosis/release 1.3.0 → 2.0.0
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/chunk-KQIWTXQP.js +2134 -0
- package/dist/index.d.ts +317 -1
- package/dist/index.js +67 -3
- package/dist/release-cli.js +273 -9
- package/package.json +3 -2
- package/dist/chunk-D5IC766I.js +0 -1006
package/dist/release-cli.js
CHANGED
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CannotRun,
|
|
3
|
+
DEFAULT_REGISTRY,
|
|
4
|
+
MIGRATIONS_NEXT,
|
|
5
|
+
PUBLISHED_NEXT,
|
|
6
|
+
SCHEMA_NEXT,
|
|
7
|
+
SMOKE_NEXT,
|
|
8
|
+
formatBaseline,
|
|
3
9
|
formatDeployed,
|
|
4
10
|
formatMigrations,
|
|
5
11
|
formatPlan,
|
|
6
12
|
formatProve,
|
|
13
|
+
formatPublished,
|
|
14
|
+
formatSchema,
|
|
15
|
+
formatSnapshot,
|
|
16
|
+
formatSweep,
|
|
7
17
|
formatVerdict,
|
|
18
|
+
migrationsEnvelope,
|
|
8
19
|
prove,
|
|
9
20
|
proveOver,
|
|
21
|
+
publishedEnvelope,
|
|
22
|
+
readReleaseConfig,
|
|
23
|
+
recordBaseline,
|
|
10
24
|
runDeployed,
|
|
11
25
|
runMigrations,
|
|
12
|
-
runPlan
|
|
13
|
-
|
|
26
|
+
runPlan,
|
|
27
|
+
runPublished,
|
|
28
|
+
runSchema,
|
|
29
|
+
runSnapshot,
|
|
30
|
+
schemaEnvelope,
|
|
31
|
+
sweepEnvelope,
|
|
32
|
+
sweepSmoke,
|
|
33
|
+
writeEnvelope
|
|
34
|
+
} from "./chunk-KQIWTXQP.js";
|
|
14
35
|
|
|
15
36
|
// src/release-cli.ts
|
|
16
|
-
import {
|
|
37
|
+
import { existsSync } from "fs";
|
|
38
|
+
import { join, resolve } from "path";
|
|
17
39
|
var USAGE = `geonosis-release <command> [options]
|
|
18
40
|
|
|
19
41
|
migrations --since <ref> [--dialect <name>] [--root <dir>] [--json]
|
|
@@ -30,6 +52,17 @@ var USAGE = `geonosis-release <command> [options]
|
|
|
30
52
|
|
|
31
53
|
Exit 0 clean, 1 refusals, 2 the run could not be made.
|
|
32
54
|
|
|
55
|
+
schema [--root <dir>] [--json]
|
|
56
|
+
|
|
57
|
+
The wall no oxlint rule can see. Over every migration under every directory
|
|
58
|
+
geonosis.json \u2192 release.schema.domains declares: a statement naming a table another
|
|
59
|
+
domain declared is a cross-domain dependency the import graph does not show, and a
|
|
60
|
+
current_setting('\u2026') outside release.schema.sessionVariables writes one consumer's
|
|
61
|
+
session vocabulary into a package everyone installs.
|
|
62
|
+
|
|
63
|
+
Options only, empty defaults; the block declared and empty is a refusal naming both.
|
|
64
|
+
Whole-directory, not diff-scoped \u2014 a domain's migrations are applied as a set.
|
|
65
|
+
|
|
33
66
|
plan [--root <dir>] [--i-accept-an-unproven-smoke] [--json]
|
|
34
67
|
|
|
35
68
|
The contract as data \u2014 geonosis.json \u2192 release.steps and release.proof \u2014 validated
|
|
@@ -53,13 +86,67 @@ var USAGE = `geonosis-release <command> [options]
|
|
|
53
86
|
tree declares in the wrangler configs geonosis.json names and release.secrets.
|
|
54
87
|
\`versions upload\` applies no triggers, and a secret can be put outside the gate
|
|
55
88
|
entirely \u2014 so declared and deployed drift apart silently, with a green pipeline.
|
|
89
|
+
|
|
90
|
+
published --group [--registry <url>] [--wait <seconds>] [--root <dir>] [--json]
|
|
91
|
+
|
|
92
|
+
Every workspace's local version against the version the registry answers, with no
|
|
93
|
+
sampling. \`changeset publish\` exiting zero is not the release; the GROUP resolving
|
|
94
|
+
from npm is \u2014 a six-package sample announced a version while the one unchecked entry
|
|
95
|
+
point read one release behind, and the consumer's bump aborted on it and left a
|
|
96
|
+
nested duplicate (#147).
|
|
97
|
+
|
|
98
|
+
The registry is asked over HTTP, never through npm or bun: their manifest CACHE is
|
|
99
|
+
the thing that lied. --wait polls through the few minutes in which a freshly
|
|
100
|
+
published name still 404s.
|
|
101
|
+
|
|
102
|
+
Exit 0 the whole group is there, 1 a gap, 2 the registry could not be asked.
|
|
103
|
+
|
|
104
|
+
smoke snapshot <name> --from <their tree> [--replace] [--root <dir>] [--json]
|
|
105
|
+
[--typecheck <cmd>] [--lint <cmd>] [--doctor <cmd>]
|
|
106
|
+
|
|
107
|
+
Copy a consumer tree, read-only, into .geonosis/consumer-snapshots/<name>/ \u2014 the
|
|
108
|
+
thing a release must be read against. Installed packages, build output and the git
|
|
109
|
+
history stay behind; the manager comes off the lockfile; the versions every manifest
|
|
110
|
+
declares and the commands the smoke will run are recorded beside the copy.
|
|
111
|
+
|
|
112
|
+
The bytes are a private tree's (D-048), so it refuses to write where git would let
|
|
113
|
+
them be committed.
|
|
114
|
+
|
|
115
|
+
smoke baseline --snapshot <name> [--rc-dir <workspace>] [--install <cmd>] [--json]
|
|
116
|
+
smoke run --snapshot <name> [--rc-dir <workspace>] [--install <cmd>] [--json]
|
|
117
|
+
|
|
118
|
+
Pack every workspace package of the release candidate (\`pnpm pack\` \u2014 a tarball is
|
|
119
|
+
what a consumer installs, and a release that shipped no declarations shipped them in
|
|
120
|
+
the tree and not in the tarball), point a COPY of the snapshot's manifests at those
|
|
121
|
+
tarballs, install with the snapshot's own manager, and run the snapshot's own
|
|
122
|
+
typecheck, lint and doctor.
|
|
123
|
+
|
|
124
|
+
\`baseline\` records what those three answered at the versions the consumer is on.
|
|
125
|
+
\`run\` compares against it and refuses only what THIS release broke, in their
|
|
126
|
+
compiler's and their doctor's own lines. Exit 0 nothing new, 1 a new failure,
|
|
127
|
+
2 the run could not be made \u2014 including a phase the baseline cannot be compared to.
|
|
56
128
|
`;
|
|
57
|
-
var VALUED = /* @__PURE__ */ new Set([
|
|
58
|
-
|
|
129
|
+
var VALUED = /* @__PURE__ */ new Set([
|
|
130
|
+
"--dialect",
|
|
131
|
+
"--doctor",
|
|
132
|
+
"--from",
|
|
133
|
+
"--install",
|
|
134
|
+
"--lint",
|
|
135
|
+
"--rc-dir",
|
|
136
|
+
"--registry",
|
|
137
|
+
"--root",
|
|
138
|
+
"--runner",
|
|
139
|
+
"--since",
|
|
140
|
+
"--snapshot",
|
|
141
|
+
"--typecheck",
|
|
142
|
+
"--wait"
|
|
143
|
+
]);
|
|
144
|
+
var FLAGS = /* @__PURE__ */ new Set(["--check", "--group", "--i-accept-an-unproven-smoke", "--json", "--replace"]);
|
|
59
145
|
var DIALECTS = /* @__PURE__ */ new Set(["mikro-orm-ts", "postgres", "sqlite"]);
|
|
60
146
|
var parseArgs = (argv) => {
|
|
61
147
|
const read = {};
|
|
62
148
|
const flags = /* @__PURE__ */ new Set();
|
|
149
|
+
const rest = [];
|
|
63
150
|
let json = false;
|
|
64
151
|
for (let index = 1; index < argv.length; index += 1) {
|
|
65
152
|
const flag = argv[index] ?? "";
|
|
@@ -68,13 +155,17 @@ var parseArgs = (argv) => {
|
|
|
68
155
|
flags.add(flag);
|
|
69
156
|
continue;
|
|
70
157
|
}
|
|
158
|
+
if (!flag.startsWith("--")) {
|
|
159
|
+
rest.push(flag);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
71
162
|
if (!VALUED.has(flag)) throw new CannotRun(`unknown argument "${flag}"`);
|
|
72
163
|
const value = argv[index + 1];
|
|
73
164
|
if (value === void 0 || value.startsWith("--")) throw new CannotRun(`${flag} needs a value`);
|
|
74
165
|
read[flag] = value;
|
|
75
166
|
index += 1;
|
|
76
167
|
}
|
|
77
|
-
return { command: argv[0] ?? "", flags, json, read };
|
|
168
|
+
return { command: argv[0] ?? "", flags, json, read, rest };
|
|
78
169
|
};
|
|
79
170
|
var rootOf = (parsed, cwd) => resolve(cwd, parsed.read["--root"] ?? cwd);
|
|
80
171
|
var migrations = (parsed, cwd) => {
|
|
@@ -84,15 +175,42 @@ var migrations = (parsed, cwd) => {
|
|
|
84
175
|
if (dialect !== void 0 && !DIALECTS.has(dialect)) {
|
|
85
176
|
throw new CannotRun(`--dialect must be one of ${[...DIALECTS].toSorted().join(", ")}`);
|
|
86
177
|
}
|
|
178
|
+
const root = resolve(cwd, parsed.read["--root"] ?? cwd);
|
|
179
|
+
const startedAt = Date.now();
|
|
87
180
|
const report = runMigrations({
|
|
88
181
|
...dialect === void 0 ? {} : { dialect },
|
|
89
|
-
root
|
|
182
|
+
root,
|
|
90
183
|
since
|
|
91
184
|
});
|
|
92
185
|
process.stdout.write(
|
|
93
186
|
parsed.json ? `${JSON.stringify(report, void 0, 2)}
|
|
94
187
|
` : formatMigrations(report)
|
|
95
188
|
);
|
|
189
|
+
const at = writeEnvelope({
|
|
190
|
+
envelope: migrationsEnvelope(report, Date.now() - startedAt),
|
|
191
|
+
next: MIGRATIONS_NEXT,
|
|
192
|
+
root
|
|
193
|
+
});
|
|
194
|
+
if (!parsed.json) process.stdout.write(`envelope: ${at}
|
|
195
|
+
`);
|
|
196
|
+
if (report.unreadable.length > 0) return 2;
|
|
197
|
+
return report.ok ? 0 : 1;
|
|
198
|
+
};
|
|
199
|
+
var schema = (parsed, cwd) => {
|
|
200
|
+
const root = rootOf(parsed, cwd);
|
|
201
|
+
const startedAt = Date.now();
|
|
202
|
+
const report = runSchema({ root });
|
|
203
|
+
process.stdout.write(
|
|
204
|
+
parsed.json ? `${JSON.stringify(report, void 0, 2)}
|
|
205
|
+
` : formatSchema(report)
|
|
206
|
+
);
|
|
207
|
+
const at = writeEnvelope({
|
|
208
|
+
envelope: schemaEnvelope(report, Date.now() - startedAt),
|
|
209
|
+
next: SCHEMA_NEXT,
|
|
210
|
+
root
|
|
211
|
+
});
|
|
212
|
+
if (!parsed.json) process.stdout.write(`envelope: ${at}
|
|
213
|
+
`);
|
|
96
214
|
if (report.unreadable.length > 0) return 2;
|
|
97
215
|
return report.ok ? 0 : 1;
|
|
98
216
|
};
|
|
@@ -134,6 +252,137 @@ var deployed = (parsed, cwd) => {
|
|
|
134
252
|
);
|
|
135
253
|
return report.ok ? 0 : 1;
|
|
136
254
|
};
|
|
255
|
+
var published = async (parsed, cwd) => {
|
|
256
|
+
if (!parsed.flags.has("--group")) {
|
|
257
|
+
throw new CannotRun("published needs --group \u2014 the whole workspace, or nothing");
|
|
258
|
+
}
|
|
259
|
+
const wait = parsed.read["--wait"];
|
|
260
|
+
if (wait !== void 0 && !/^\d+(?:\.\d+)?$/.test(wait)) {
|
|
261
|
+
throw new CannotRun(`--wait takes a number of seconds, not "${wait}"`);
|
|
262
|
+
}
|
|
263
|
+
const root = rootOf(parsed, cwd);
|
|
264
|
+
const startedAt = Date.now();
|
|
265
|
+
const report = await runPublished({
|
|
266
|
+
registry: parsed.read["--registry"] ?? DEFAULT_REGISTRY,
|
|
267
|
+
root,
|
|
268
|
+
waitSeconds: wait === void 0 ? 0 : Number(wait)
|
|
269
|
+
});
|
|
270
|
+
process.stdout.write(
|
|
271
|
+
parsed.json ? `${JSON.stringify(report, void 0, 2)}
|
|
272
|
+
` : formatPublished(report)
|
|
273
|
+
);
|
|
274
|
+
const at = writeEnvelope({
|
|
275
|
+
envelope: publishedEnvelope(report, Date.now() - startedAt),
|
|
276
|
+
next: PUBLISHED_NEXT,
|
|
277
|
+
root
|
|
278
|
+
});
|
|
279
|
+
if (!parsed.json) process.stdout.write(`envelope: ${at}
|
|
280
|
+
`);
|
|
281
|
+
if (report.unreachable > 0) return 2;
|
|
282
|
+
return report.ok ? 0 : 1;
|
|
283
|
+
};
|
|
284
|
+
var namedCommands = (parsed) => ({
|
|
285
|
+
...parsed.read["--doctor"] === void 0 ? {} : { doctor: parsed.read["--doctor"] },
|
|
286
|
+
...parsed.read["--lint"] === void 0 ? {} : { lint: parsed.read["--lint"] },
|
|
287
|
+
...parsed.read["--typecheck"] === void 0 ? {} : { typecheck: parsed.read["--typecheck"] }
|
|
288
|
+
});
|
|
289
|
+
var snapshot = (parsed, root) => {
|
|
290
|
+
const name = parsed.rest[1];
|
|
291
|
+
if (name === void 0) {
|
|
292
|
+
throw new CannotRun("smoke snapshot needs a name: smoke snapshot <name> --from <their tree>");
|
|
293
|
+
}
|
|
294
|
+
const from = parsed.read["--from"];
|
|
295
|
+
if (from === void 0) {
|
|
296
|
+
throw new CannotRun("smoke snapshot needs --from <the consumer tree to copy>");
|
|
297
|
+
}
|
|
298
|
+
const record = runSnapshot({
|
|
299
|
+
exclude: readReleaseConfig(root).smoke?.exclude ?? [],
|
|
300
|
+
from,
|
|
301
|
+
name,
|
|
302
|
+
named: namedCommands(parsed),
|
|
303
|
+
replace: parsed.flags.has("--replace"),
|
|
304
|
+
root
|
|
305
|
+
});
|
|
306
|
+
process.stdout.write(
|
|
307
|
+
parsed.json ? `${JSON.stringify(record, void 0, 2)}
|
|
308
|
+
` : formatSnapshot(record)
|
|
309
|
+
);
|
|
310
|
+
return 0;
|
|
311
|
+
};
|
|
312
|
+
var wantedSnapshots = (parsed, root) => {
|
|
313
|
+
const configured = readReleaseConfig(root).smoke?.snapshots ?? [];
|
|
314
|
+
const named = parsed.read["--snapshot"];
|
|
315
|
+
const install = parsed.read["--install"];
|
|
316
|
+
const withFlag = (one) => {
|
|
317
|
+
const chosen = install ?? one.install;
|
|
318
|
+
return { ...chosen === void 0 ? {} : { install: chosen }, name: one.name };
|
|
319
|
+
};
|
|
320
|
+
if (named === void 0) return configured.map(withFlag);
|
|
321
|
+
return [withFlag(configured.find((one) => one.name === named) ?? { name: named })];
|
|
322
|
+
};
|
|
323
|
+
var rcOf = (parsed, root, cwd) => {
|
|
324
|
+
const named = parsed.read["--rc-dir"];
|
|
325
|
+
if (named !== void 0) return resolve(cwd, named);
|
|
326
|
+
if (existsSync(join(root, "package.json"))) return root;
|
|
327
|
+
throw new CannotRun(
|
|
328
|
+
"nothing here is a release candidate \u2014 pass --rc-dir <the workspace whose packages this release publishes>, because a smoke with nothing packed compares a tree to itself"
|
|
329
|
+
);
|
|
330
|
+
};
|
|
331
|
+
var baseline = (parsed, root, cwd) => {
|
|
332
|
+
const wanted = wantedSnapshots(parsed, root);
|
|
333
|
+
const one = wanted[0];
|
|
334
|
+
if (parsed.read["--snapshot"] === void 0 || one === void 0) {
|
|
335
|
+
throw new CannotRun("smoke baseline needs --snapshot <name>");
|
|
336
|
+
}
|
|
337
|
+
const rc = parsed.read["--rc-dir"];
|
|
338
|
+
const recorded = recordBaseline({
|
|
339
|
+
...one.install === void 0 ? {} : { install: one.install },
|
|
340
|
+
name: one.name,
|
|
341
|
+
...rc === void 0 ? {} : { rc: resolve(cwd, rc) },
|
|
342
|
+
root
|
|
343
|
+
});
|
|
344
|
+
process.stdout.write(
|
|
345
|
+
parsed.json ? `${JSON.stringify(recorded, void 0, 2)}
|
|
346
|
+
` : formatBaseline(one.name, recorded)
|
|
347
|
+
);
|
|
348
|
+
return 0;
|
|
349
|
+
};
|
|
350
|
+
var smokeRun = (parsed, root, cwd) => {
|
|
351
|
+
const startedAt = Date.now();
|
|
352
|
+
const wanted = wantedSnapshots(parsed, root);
|
|
353
|
+
if (wanted.length === 0) {
|
|
354
|
+
throw new CannotRun(
|
|
355
|
+
"nothing here names a consumer to read this release against \u2014 geonosis.json \u2192 release.smoke.snapshots names them, or pass --snapshot <name>. A release check over zero consumers is not a green release check."
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
const swept = sweepSmoke({
|
|
359
|
+
rc: rcOf(parsed, root, cwd),
|
|
360
|
+
root,
|
|
361
|
+
skipMissing: parsed.read["--snapshot"] === void 0,
|
|
362
|
+
wanted
|
|
363
|
+
});
|
|
364
|
+
process.stdout.write(
|
|
365
|
+
parsed.json ? `${JSON.stringify(swept, void 0, 2)}
|
|
366
|
+
` : formatSweep(swept)
|
|
367
|
+
);
|
|
368
|
+
const at = writeEnvelope({
|
|
369
|
+
envelope: sweepEnvelope(swept, Date.now() - startedAt),
|
|
370
|
+
next: SMOKE_NEXT,
|
|
371
|
+
root
|
|
372
|
+
});
|
|
373
|
+
if (!parsed.json) process.stdout.write(`envelope: ${at}
|
|
374
|
+
`);
|
|
375
|
+
if (swept.comparisons.some((one) => one.refused.length > 0)) return 2;
|
|
376
|
+
return swept.comparisons.every((one) => one.ok) ? 0 : 1;
|
|
377
|
+
};
|
|
378
|
+
var smoke = (parsed, cwd) => {
|
|
379
|
+
const root = rootOf(parsed, cwd);
|
|
380
|
+
const verb = parsed.rest[0];
|
|
381
|
+
if (verb === "snapshot") return snapshot(parsed, root);
|
|
382
|
+
if (verb === "baseline") return baseline(parsed, root, cwd);
|
|
383
|
+
if (verb === "run") return smokeRun(parsed, root, cwd);
|
|
384
|
+
throw new CannotRun(`smoke has snapshot, baseline and run \u2014 "${verb ?? ""}" is none of them`);
|
|
385
|
+
};
|
|
137
386
|
var CONFIG_SHAPE = `geonosis-release reads geonosis.json
|
|
138
387
|
|
|
139
388
|
geonosis.json \u2192 "release" every key optional; a repo that configured nothing gets nothing
|
|
@@ -143,6 +392,13 @@ geonosis.json \u2192 "release" every key optional; a repo that configured n
|
|
|
143
392
|
phases string[]? "up" and/or "down" (default ["up"])
|
|
144
393
|
squawk object? { exclude: string[] } \u2014 the rule ids to leave out
|
|
145
394
|
proof object? { mustAssertVersion: boolean }
|
|
395
|
+
schema object? the walls no oxlint rule can see (#168)
|
|
396
|
+
domains Entry[]? { dir, name, tables } \u2014 whose migrations, and whose tables
|
|
397
|
+
sessionVariables string[]? the session variable names DDL may name
|
|
398
|
+
smoke object? the consumer trees this release is read against
|
|
399
|
+
exclude string[]? what a snapshot leaves behind, beyond the built and the installed
|
|
400
|
+
snapshots Entry[]? { name, install? } \u2014 the recorded snapshot, and the command that
|
|
401
|
+
tree is installed with when the manager's default is not it
|
|
146
402
|
secrets string[]? the secret names a deployment carries
|
|
147
403
|
steps string[]? the release steps
|
|
148
404
|
workers string[]? the workers it deploys
|
|
@@ -150,7 +406,10 @@ geonosis.json \u2192 "release" every key optional; a repo that configured n
|
|
|
150
406
|
wranglerEnv string? the named environment block to read out of them
|
|
151
407
|
|
|
152
408
|
A key outside either list is refused BY NAME, with the accepted set. This shape had to be read out
|
|
153
|
-
of a bundled chunk file by a consumer once (#75)
|
|
409
|
+
of a bundled chunk file by a consumer once (#75).
|
|
410
|
+
|
|
411
|
+
published reads no geonosis.json at all: its inputs are every package.json under the root \u2014 "name"
|
|
412
|
+
and "version", and "private" to tell a package a publish is a claim about from one it is not.`;
|
|
154
413
|
var main = async () => {
|
|
155
414
|
const argv = process.argv.slice(2);
|
|
156
415
|
if (argv.includes("--print-config-shape")) {
|
|
@@ -163,12 +422,17 @@ var main = async () => {
|
|
|
163
422
|
return 0;
|
|
164
423
|
}
|
|
165
424
|
const parsed = parseArgs(argv);
|
|
425
|
+
if (parsed.command === "smoke") return smoke(parsed, process.cwd());
|
|
426
|
+
const stray = parsed.rest[0];
|
|
427
|
+
if (stray !== void 0) throw new CannotRun(`unknown argument "${stray}"`);
|
|
166
428
|
if (parsed.command === "migrations") return migrations(parsed, process.cwd());
|
|
167
429
|
if (parsed.command === "plan") return plan(parsed, process.cwd());
|
|
430
|
+
if (parsed.command === "schema") return schema(parsed, process.cwd());
|
|
168
431
|
if (parsed.command === "prove") return proving(parsed, process.cwd());
|
|
169
432
|
if (parsed.command === "deployed") return deployed(parsed, process.cwd());
|
|
433
|
+
if (parsed.command === "published") return published(parsed, process.cwd());
|
|
170
434
|
throw new CannotRun(
|
|
171
|
-
`no command called "${parsed.command}" \u2014 this bin has migrations, plan, prove, deployed`
|
|
435
|
+
`no command called "${parsed.command}" \u2014 this bin has migrations, plan, prove, deployed, published`
|
|
172
436
|
);
|
|
173
437
|
};
|
|
174
438
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geonosis/release",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"types": "./dist/index.d.ts",
|
|
5
5
|
"description": "The release contract with its proofs: an expand-only migration gate over three dialects, a smoke that must name the version that answered it, and declared ≠ deployed.",
|
|
6
6
|
"keywords": [
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"squawk-cli": "2.63.0"
|
|
47
|
+
"squawk-cli": "2.63.0",
|
|
48
|
+
"@geonosis/ratchet": "2.0.0"
|
|
48
49
|
},
|
|
49
50
|
"engines": {
|
|
50
51
|
"node": ">=22"
|