@pd4castr/cli 1.10.0 → 1.12.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.
Files changed (3) hide show
  1. package/README.md +1 -6
  2. package/dist/index.js +243 -80
  3. package/package.json +6 -6
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  <p>
12
12
  <a href="#installation">Installation</a> •
13
13
  <a href="#quick-start">Quick Start</a> •
14
- <a href="https://github.com/pipelabs/pd4castr-model-examples/" target="_blank">Full Documentation</a>
14
+ <a href="https://docs.v2.pd4castr.com.au/authoring/install-the-cli">Documentation</a>
15
15
  </p>
16
16
  </div>
17
17
 
@@ -48,8 +48,3 @@ Publish your model to the pd4castr platform:
48
48
  ```sh
49
49
  pd4castr publish
50
50
  ```
51
-
52
- ## Contributing
53
-
54
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, testing, and
55
- contribution guidelines.
package/dist/index.js CHANGED
@@ -90,6 +90,7 @@ var modelInputSchema = z.union([
90
90
  ]);
91
91
  var modelOutputSchema = z.object({
92
92
  name: z.string(),
93
+ key: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/).optional(),
93
94
  type: z.enum([
94
95
  "float",
95
96
  "integer",
@@ -103,6 +104,7 @@ var modelOutputSchema = z.object({
103
104
  });
104
105
  var sensitivitySchema = z.object({
105
106
  name: z.string(),
107
+ key: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/).optional(),
106
108
  query: z.string()
107
109
  });
108
110
  var inputAggregationSchema = z.object({
@@ -132,10 +134,45 @@ var projectConfigSchema = z.object({
132
134
  outputs: z.array(modelOutputSchema),
133
135
  sensitivities: z.array(sensitivitySchema).optional().default([]),
134
136
  [CONFIG_WARNING_KEY]: z.string().optional().default(""),
137
+ $$configVersion: z.number().int().min(0).optional().default(0),
135
138
  $$id: z.string().nullable().optional().default(null),
136
139
  $$modelGroupID: z.string().nullable().optional().default(null),
137
140
  $$revision: z.number().int().min(0).nullable().optional().default(null),
138
141
  $$dockerImage: z.string().nullable().optional().default(null)
142
+ }).superRefine((data, ctx) => {
143
+ if (data.$$id) {
144
+ return;
145
+ }
146
+ for (const [i, output] of data.outputs.entries()) {
147
+ if (!output.key) {
148
+ ctx.addIssue({
149
+ code: "invalid_format",
150
+ format: "regex",
151
+ pattern: "^[a-z0-9][a-z0-9_-]*$",
152
+ path: [
153
+ "outputs",
154
+ i,
155
+ "key"
156
+ ],
157
+ message: "key is required (lowercase alphanumeric with hyphens/underscores)"
158
+ });
159
+ }
160
+ }
161
+ for (const [i, sensitivity] of data.sensitivities.entries()) {
162
+ if (!sensitivity.key) {
163
+ ctx.addIssue({
164
+ code: "invalid_format",
165
+ format: "regex",
166
+ pattern: "^[a-z0-9][a-z0-9_-]*$",
167
+ path: [
168
+ "sensitivities",
169
+ i,
170
+ "key"
171
+ ],
172
+ message: "key is required (lowercase alphanumeric with hyphens/underscores)"
173
+ });
174
+ }
175
+ }
139
176
  });
140
177
 
141
178
  // src/utils/is-existing-path.ts
@@ -164,6 +201,7 @@ async function loadProjectContext(configPath) {
164
201
  const config = projectConfigSchema.parse(rawConfig);
165
202
  return {
166
203
  config,
204
+ configPath: resolvedConfigPath,
167
205
  projectRoot
168
206
  };
169
207
  } catch (error) {
@@ -298,9 +336,83 @@ function getEnv() {
298
336
  }
299
337
  __name(getEnv, "getEnv");
300
338
 
339
+ // package.json
340
+ var package_default = {
341
+ name: "@pd4castr/cli",
342
+ version: "1.12.0",
343
+ description: "CLI tool for creating, testing, and publishing pd4castr models",
344
+ license: "MIT",
345
+ main: "dist/index.js",
346
+ private: false,
347
+ type: "module",
348
+ bin: {
349
+ pd4castr: "dist/index.js"
350
+ },
351
+ files: [
352
+ "dist/**/*",
353
+ "LICENSE.md",
354
+ "docs/assets/logo.png"
355
+ ],
356
+ engines: {
357
+ node: ">=20.0.0"
358
+ },
359
+ scripts: {
360
+ build: "tsup",
361
+ dev: "tsup --watch",
362
+ pd4castr: "node dist/index.js",
363
+ format: "prettier --write .",
364
+ lint: "eslint .",
365
+ typecheck: "tsc --noEmit",
366
+ prepublishOnly: "pnpm run build"
367
+ },
368
+ dependencies: {
369
+ "@inquirer/prompts": "7.7.1",
370
+ auth0: "4.28.0",
371
+ chalk: "5.6.0",
372
+ commander: "14.0.0",
373
+ execa: "9.6.0",
374
+ express: "4.21.2",
375
+ immer: "10.1.1",
376
+ ky: "1.14.2",
377
+ ora: "8.2.0",
378
+ "promise-retry": "2.0.1",
379
+ slugify: "1.6.6",
380
+ tiged: "2.12.7",
381
+ "tiny-invariant": "1.3.3",
382
+ zod: "4.0.14"
383
+ },
384
+ devDependencies: {
385
+ "@faker-js/faker": "10.0.0",
386
+ "@mswjs/data": "0.16.2",
387
+ "@types/express": "4.17.21",
388
+ "@types/node": "24.1.0",
389
+ "@types/supertest": "6.0.3",
390
+ "hook-std": "3.0.0",
391
+ "jest-extended": "7.0.0",
392
+ memfs: "4.49.0",
393
+ msw: "2.12.9",
394
+ "strip-ansi": "7.1.0",
395
+ supertest: "7.2.2",
396
+ tsup: "8.5.0",
397
+ "type-fest": "4.41.0",
398
+ typescript: "5.8.3",
399
+ vitest: "4.0.18"
400
+ },
401
+ publishConfig: {
402
+ access: "public"
403
+ },
404
+ keywords: [
405
+ "cli",
406
+ "pd4castr"
407
+ ]
408
+ };
409
+
301
410
  // src/api/api.ts
302
411
  var api = ky.create({
303
- prefixUrl: getEnv().apiURL
412
+ prefixUrl: getEnv().apiURL,
413
+ headers: {
414
+ "X-SDK-Version": `cli/${package_default.version}`
415
+ }
304
416
  });
305
417
 
306
418
  // src/api/query-data-fetcher.ts
@@ -840,7 +952,7 @@ async function getModelConfigFromProjectConfig(ctx) {
840
952
  const sensitivities = await getSensitivitiesWithInlinedSQL(ctx);
841
953
  const inputAggregations = await getInputAggregationsWithInlinedSQL(ctx);
842
954
  const runDatetimeQuery = await getrunDatetimeQuerySQL(ctx);
843
- const { $$id, $$modelGroupID, $$revision, $$dockerImage, ...config } = ctx.config;
955
+ const { $$configVersion, $$id, $$modelGroupID, $$revision, $$dockerImage, ...config } = ctx.config;
844
956
  return {
845
957
  ...config,
846
958
  id: $$id,
@@ -1008,7 +1120,7 @@ function getModelSummaryLines(ctx) {
1008
1120
  ] : [],
1009
1121
  ...ctx.config.sensitivities.length > 0 ? [
1010
1122
  ` ${chalk2.bold("Sensitivities:")}`,
1011
- ...ctx.config.sensitivities.map((s) => ` \u2022 ${s.name}`)
1123
+ ...ctx.config.sensitivities.map((s) => ` \u2022 ${s.name}${s.key ? ` (${s.key})` : ""}`)
1012
1124
  ] : [],
1013
1125
  ""
1014
1126
  ];
@@ -1405,8 +1517,125 @@ import chalk6 from "chalk";
1405
1517
  import * as inquirer3 from "@inquirer/prompts";
1406
1518
  import chalk4 from "chalk";
1407
1519
 
1408
- // src/commands/publish/utils/validate-local-model-state.ts
1520
+ // src/utils/migrate-config.ts
1521
+ import fs11 from "fs/promises";
1522
+ import invariant4 from "tiny-invariant";
1523
+
1524
+ // src/migrations/01-backfill-output-variable-keys.ts
1409
1525
  import invariant2 from "tiny-invariant";
1526
+ var backfillOutputVariableKeys = {
1527
+ version: 1,
1528
+ description: "Backfill output variable keys from published model",
1529
+ migrate(config, ctx) {
1530
+ const outputsMissingKeys = config.outputs.filter((output) => !output.key);
1531
+ if (outputsMissingKeys.length === 0) {
1532
+ return config;
1533
+ }
1534
+ invariant2(ctx.publishedModel, "Published model is required to backfill output variable keys");
1535
+ const publishedKeysByName = /* @__PURE__ */ new Map();
1536
+ for (const published of ctx.publishedModel.outputVariables) {
1537
+ publishedKeysByName.set(published.name, published.key);
1538
+ }
1539
+ const unmatchedOutputs = [];
1540
+ const updatedOutputs = config.outputs.map((output) => {
1541
+ if (output.key) {
1542
+ return output;
1543
+ }
1544
+ const publishedKey = publishedKeysByName.get(output.name);
1545
+ if (publishedKey) {
1546
+ return {
1547
+ ...output,
1548
+ key: publishedKey
1549
+ };
1550
+ }
1551
+ unmatchedOutputs.push(output.name);
1552
+ return output;
1553
+ });
1554
+ if (unmatchedOutputs.length > 0) {
1555
+ throw new Error(`The following outputs need a \`key\` defined in .pd4castrrc.json:
1556
+ ${unmatchedOutputs.map((name) => ` - ${name}`).join("\n")}
1557
+
1558
+ Set a unique key (lowercase alphanumeric with hyphens/underscores) for each output.`);
1559
+ }
1560
+ return {
1561
+ ...config,
1562
+ outputs: updatedOutputs
1563
+ };
1564
+ }
1565
+ };
1566
+
1567
+ // src/migrations/02-backfill-sensitivity-keys.ts
1568
+ import invariant3 from "tiny-invariant";
1569
+ var backfillSensitivityKeys = {
1570
+ version: 2,
1571
+ description: "Backfill sensitivity keys from published model",
1572
+ migrate(config, ctx) {
1573
+ const sensitivitiesMissingKeys = (config.sensitivities ?? []).filter((sensitivity) => !sensitivity.key);
1574
+ if (sensitivitiesMissingKeys.length === 0) {
1575
+ return config;
1576
+ }
1577
+ invariant3(ctx.publishedModel, "Published model is required to backfill sensitivity keys");
1578
+ const publishedKeysByName = /* @__PURE__ */ new Map();
1579
+ for (const published of ctx.publishedModel.sensitivities) {
1580
+ publishedKeysByName.set(published.name, published.key);
1581
+ }
1582
+ const unmatchedSensitivities = [];
1583
+ const updatedSensitivities = config.sensitivities.map((sensitivity) => {
1584
+ if (sensitivity.key) {
1585
+ return sensitivity;
1586
+ }
1587
+ const publishedKey = publishedKeysByName.get(sensitivity.name);
1588
+ if (publishedKey) {
1589
+ return {
1590
+ ...sensitivity,
1591
+ key: publishedKey
1592
+ };
1593
+ }
1594
+ unmatchedSensitivities.push(sensitivity.name);
1595
+ return sensitivity;
1596
+ });
1597
+ if (unmatchedSensitivities.length > 0) {
1598
+ throw new Error(`The following sensitivities need a \`key\` defined in .pd4castrrc.json:
1599
+ ${unmatchedSensitivities.map((name) => ` - ${name}`).join("\n")}
1600
+
1601
+ Set a unique key (lowercase alphanumeric with hyphens/underscores) for each sensitivity.`);
1602
+ }
1603
+ return {
1604
+ ...config,
1605
+ sensitivities: updatedSensitivities
1606
+ };
1607
+ }
1608
+ };
1609
+
1610
+ // src/migrations/index.ts
1611
+ var migrations = [
1612
+ backfillOutputVariableKeys,
1613
+ backfillSensitivityKeys
1614
+ ];
1615
+
1616
+ // src/utils/migrate-config.ts
1617
+ async function migrateConfig(ctx, migrationCtx) {
1618
+ const pendingMigrations = migrations.filter((migration) => migration.version > ctx.config.$$configVersion).sort((a, b) => a.version - b.version);
1619
+ if (pendingMigrations.length === 0) {
1620
+ return;
1621
+ }
1622
+ let config = ctx.config;
1623
+ for (const migration of pendingMigrations) {
1624
+ config = migration.migrate(config, migrationCtx);
1625
+ }
1626
+ const lastMigration = pendingMigrations.at(-1);
1627
+ invariant4(lastMigration, "expected at least one pending migration");
1628
+ config = {
1629
+ ...config,
1630
+ $$configVersion: lastMigration.version
1631
+ };
1632
+ ctx.config = config;
1633
+ await fs11.writeFile(ctx.configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
1634
+ }
1635
+ __name(migrateConfig, "migrateConfig");
1636
+
1637
+ // src/commands/publish/utils/validate-local-model-state.ts
1638
+ import invariant5 from "tiny-invariant";
1410
1639
 
1411
1640
  // src/api/get-model.ts
1412
1641
  async function getModel(modelId, authCtx) {
@@ -1422,7 +1651,7 @@ __name(getModel, "getModel");
1422
1651
 
1423
1652
  // src/commands/publish/utils/validate-local-model-state.ts
1424
1653
  async function validateLocalModelState(ctx, authCtx) {
1425
- invariant2(ctx.config.$$id, "model ID is required to fetch published model");
1654
+ invariant5(ctx.config.$$id, "model ID is required to fetch published model");
1426
1655
  const currentModel = await getModel(ctx.config.$$id, authCtx);
1427
1656
  if (currentModel.revision !== ctx.config.$$revision) {
1428
1657
  throw new Error(`OUT OF SYNC: Local revision (${ctx.config.$$revision}) does not match the current published revision (${currentModel.revision})`);
@@ -1430,6 +1659,7 @@ async function validateLocalModelState(ctx, authCtx) {
1430
1659
  if (currentModel.modelGroupId !== ctx.config.$$modelGroupID) {
1431
1660
  throw new Error(`OUT OF SYNC: Local model group ID (${ctx.config.$$modelGroupID}) does not match the current published model group ID (${currentModel.modelGroupId})`);
1432
1661
  }
1662
+ return currentModel;
1433
1663
  }
1434
1664
  __name(validateLocalModelState, "validateLocalModelState");
1435
1665
 
@@ -1449,7 +1679,10 @@ async function handleModelRevisionCreateFlow(options, app, spinner, ctx, authCtx
1449
1679
  throw new Error("Model revision update cancelled");
1450
1680
  }
1451
1681
  spinner.start("Validating local model state...");
1452
- await validateLocalModelState(ctx, authCtx);
1682
+ const publishedModel = await validateLocalModelState(ctx, authCtx);
1683
+ await migrateConfig(ctx, {
1684
+ publishedModel
1685
+ });
1453
1686
  spinner.succeed("Local model is synced with published model");
1454
1687
  const dockerImage = getDockerImage(ctx);
1455
1688
  if (!options.skipChecks) {
@@ -1537,7 +1770,10 @@ async function handleModelRevisionUpdateFlow(options, app, spinner, ctx, authCtx
1537
1770
  throw new Error("Model revision update cancelled");
1538
1771
  }
1539
1772
  spinner.start("Validating local model state...");
1540
- await validateLocalModelState(ctx, authCtx);
1773
+ const publishedModel = await validateLocalModelState(ctx, authCtx);
1774
+ await migrateConfig(ctx, {
1775
+ publishedModel
1776
+ });
1541
1777
  spinner.succeed("Local model is synced with published model");
1542
1778
  const dockerImage = getDockerImage(ctx);
1543
1779
  if (!options.skipChecks) {
@@ -1903,79 +2139,6 @@ __name(registerTestCommand, "registerTestCommand");
1903
2139
 
1904
2140
  // src/program.ts
1905
2141
  import { Command } from "commander";
1906
-
1907
- // package.json
1908
- var package_default = {
1909
- name: "@pd4castr/cli",
1910
- version: "1.10.0",
1911
- description: "CLI tool for creating, testing, and publishing pd4castr models",
1912
- license: "MIT",
1913
- main: "dist/index.js",
1914
- private: false,
1915
- type: "module",
1916
- bin: {
1917
- pd4castr: "dist/index.js"
1918
- },
1919
- files: [
1920
- "dist/**/*",
1921
- "LICENSE.md",
1922
- "docs/assets/logo.png"
1923
- ],
1924
- engines: {
1925
- node: ">=20.0.0"
1926
- },
1927
- scripts: {
1928
- build: "tsup",
1929
- dev: "tsup --watch",
1930
- pd4castr: "node dist/index.js",
1931
- format: "prettier --write .",
1932
- lint: "eslint .",
1933
- typecheck: "tsc --noEmit",
1934
- prepublishOnly: "yarn build"
1935
- },
1936
- dependencies: {
1937
- "@inquirer/prompts": "7.7.1",
1938
- auth0: "4.28.0",
1939
- chalk: "5.6.0",
1940
- commander: "14.0.0",
1941
- execa: "9.6.0",
1942
- express: "4.21.2",
1943
- immer: "10.1.1",
1944
- ky: "1.14.2",
1945
- ora: "8.2.0",
1946
- "promise-retry": "2.0.1",
1947
- slugify: "1.6.6",
1948
- tiged: "2.12.7",
1949
- "tiny-invariant": "1.3.3",
1950
- zod: "4.0.14"
1951
- },
1952
- devDependencies: {
1953
- "@faker-js/faker": "10.0.0",
1954
- "@mswjs/data": "0.16.2",
1955
- "@types/express": "4.17.21",
1956
- "@types/node": "24.1.0",
1957
- "@types/supertest": "6.0.3",
1958
- "hook-std": "3.0.0",
1959
- "jest-extended": "6.0.0",
1960
- memfs: "4.49.0",
1961
- msw: "2.10.5",
1962
- "strip-ansi": "7.1.0",
1963
- supertest: "7.1.4",
1964
- tsup: "8.5.0",
1965
- "type-fest": "4.41.0",
1966
- typescript: "5.8.3",
1967
- vitest: "3.2.4"
1968
- },
1969
- publishConfig: {
1970
- access: "public"
1971
- },
1972
- keywords: [
1973
- "cli",
1974
- "pd4castr"
1975
- ]
1976
- };
1977
-
1978
- // src/program.ts
1979
2142
  var program = new Command();
1980
2143
  program.name("pd4castr").description("CLI tool for pd4castr").version(package_default.version);
1981
2144
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pd4castr/cli",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "CLI tool for creating, testing, and publishing pd4castr models",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "format": "prettier --write .",
25
25
  "lint": "eslint .",
26
26
  "typecheck": "tsc --noEmit",
27
- "prepublishOnly": "yarn build"
27
+ "prepublishOnly": "pnpm run build"
28
28
  },
29
29
  "dependencies": {
30
30
  "@inquirer/prompts": "7.7.1",
@@ -49,15 +49,15 @@
49
49
  "@types/node": "24.1.0",
50
50
  "@types/supertest": "6.0.3",
51
51
  "hook-std": "3.0.0",
52
- "jest-extended": "6.0.0",
52
+ "jest-extended": "7.0.0",
53
53
  "memfs": "4.49.0",
54
- "msw": "2.10.5",
54
+ "msw": "2.12.9",
55
55
  "strip-ansi": "7.1.0",
56
- "supertest": "7.1.4",
56
+ "supertest": "7.2.2",
57
57
  "tsup": "8.5.0",
58
58
  "type-fest": "4.41.0",
59
59
  "typescript": "5.8.3",
60
- "vitest": "3.2.4"
60
+ "vitest": "4.0.18"
61
61
  },
62
62
  "publishConfig": {
63
63
  "access": "public"