@shipdit/cli 0.4.1 → 0.6.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 +21 -0
  2. package/dist/index.mjs +432 -121
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -12,3 +12,24 @@ shipd types gen
12
12
  ```
13
13
 
14
14
  `shipd login` stores a scoped management API key. Use `SHIPD_API_KEY` for CI — keys carry org/project/environment metadata.
15
+
16
+ ## Go projects
17
+
18
+ The CLI reads `shipd.config.go` directly. Declare an exported
19
+ `var Config = config.ShipdConfig{...}` using
20
+ `github.com/alasti-company/useshipd.com/sdks/go/config`. Go 1.23+ and Node.js 22.12+
21
+ are needed for CLI commands; the application runtime only needs Go.
22
+
23
+ ```bash
24
+ shipd diff
25
+ shipd push
26
+ shipd types gen
27
+ shipd pull
28
+ ```
29
+
30
+ Go input automatically selects Go output in `internal/flags/flags_gen.go`.
31
+ Generation emits per-flag accessors, key and variant constants, and `Defaults()`.
32
+ Pull writes Go definitions back to `shipd.config.go`. No JSON flag file is needed.
33
+ The definition file is evaluated on its own inside the Go module: imports and
34
+ helpers declared in that file work; sibling files are not loaded. Do not define
35
+ `main` in it. See `examples/go-examples` for a complete example.
package/dist/index.mjs CHANGED
@@ -5,7 +5,27 @@ import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { z } from "zod";
7
7
  import { createJiti } from "jiti";
8
+ import { spawn } from "node:child_process";
8
9
 
10
+ //#region src/lib/errors.ts
11
+ var CliError = class extends Error {
12
+ code;
13
+ exitCode;
14
+ details;
15
+ constructor(code, message, options) {
16
+ super(message);
17
+ this.name = "CliError";
18
+ this.code = code;
19
+ this.exitCode = options?.exitCode ?? 1;
20
+ this.details = options?.details;
21
+ }
22
+ };
23
+ function exitCodeForError(error) {
24
+ if (error instanceof CliError) return error.exitCode;
25
+ return 1;
26
+ }
27
+
28
+ //#endregion
9
29
  //#region src/lib/config.ts
10
30
  /** Hosted control plane. Used when no flag, env, project, global, or credential URL is set. */
11
31
  const DEFAULT_API_URL = "https://api.useshipd.com";
@@ -44,7 +64,7 @@ function clearCredentials() {
44
64
  function findProjectRoot(start = process.cwd()) {
45
65
  let current = path.resolve(start);
46
66
  while (true) {
47
- if (fs.existsSync(path.join(current, "shipd.config.ts")) || fs.existsSync(path.join(current, ".shipd", "config.json"))) return current;
67
+ if (fs.existsSync(path.join(current, "shipd.config.ts")) || fs.existsSync(path.join(current, "shipd.config.go")) || fs.existsSync(path.join(current, "shipd.config.json")) || fs.existsSync(path.join(current, ".shipd", "config.json"))) return current;
48
68
  const parent = path.dirname(current);
49
69
  if (parent === current) return process.cwd();
50
70
  current = parent;
@@ -60,7 +80,14 @@ function resolveRuntimeConfig(flags, options) {
60
80
  const globalConfig = loadGlobalConfig();
61
81
  const credentials = loadCredentials();
62
82
  const ci = flags.ci === true || flags.yes === true || process.env.CI === "1" || process.env.CI === "true" || process.env.SHIPD_CI === "1";
63
- const defaultConfigFile = fs.existsSync(path.join(projectRoot, "shipd.config.ts")) ? path.join(projectRoot, "shipd.config.ts") : path.join(cwd, "shipd.config.ts");
83
+ const defaultConfigFile = path.join(projectRoot, [
84
+ "shipd.config.ts",
85
+ "shipd.config.go",
86
+ "shipd.config.json"
87
+ ].find((file) => fs.existsSync(path.join(projectRoot, file))) ?? "shipd.config.ts");
88
+ const configFile = flags.flagsConfig ? path.resolve(flags.flagsConfig) : projectConfig.configFile ?? defaultConfigFile;
89
+ const typesLanguage = projectConfig.typesLanguage ?? (path.extname(configFile) === ".go" ? "go" : "typescript");
90
+ if (typesLanguage !== "typescript" && typesLanguage !== "go") throw new CliError("validation_error", "typesLanguage must be typescript or go");
64
91
  return {
65
92
  apiUrl: flags.apiUrl ?? process.env.SHIPD_API_URL ?? projectConfig.apiUrl ?? globalConfig.apiUrl ?? credentials.apiUrl ?? options?.defaultApiUrl ?? "https://api.useshipd.com",
66
93
  appUrl: process.env.SHIPD_APP_URL ?? projectConfig.appUrl ?? globalConfig.appUrl ?? "http://localhost:5173",
@@ -68,8 +95,10 @@ function resolveRuntimeConfig(flags, options) {
68
95
  org: flags.org ?? process.env.SHIPD_ORG ?? projectConfig.org ?? globalConfig.org,
69
96
  project: flags.project ?? process.env.SHIPD_PROJECT ?? projectConfig.project ?? globalConfig.project,
70
97
  env: flags.env ?? process.env.SHIPD_ENV ?? projectConfig.env ?? globalConfig.env,
71
- configFile: flags.flagsConfig ? path.resolve(flags.flagsConfig) : projectConfig.configFile ?? defaultConfigFile,
72
- typesOut: projectConfig.typesOut ?? "src/generated/flags.ts",
98
+ configFile,
99
+ typesOut: projectConfig.typesOut ?? (typesLanguage === "go" ? "internal/flags/flags_gen.go" : "src/generated/flags.ts"),
100
+ typesLanguage,
101
+ typesPackage: projectConfig.typesPackage ?? "flags",
73
102
  yes: ci,
74
103
  json: flags.json === true,
75
104
  cwd,
@@ -440,6 +469,9 @@ function A$1(e) {
440
469
  feedback: (e) => O$1(r.me.feedback.$post, e)
441
470
  },
442
471
  project: {
472
+ listTransfers: (e = {}) => O$1(i.project.listTransfers.$post, e),
473
+ requestTransfer: (e) => O$1(i.project.requestTransfer.$post, e),
474
+ decideTransfer: (e) => O$1(i.project.decideTransfer.$post, e),
443
475
  list: (e = {}) => O$1(i.project.list.$post, e),
444
476
  create: (e) => O$1(i.project.create.$post, e),
445
477
  update: (e) => O$1(i.project.update.$post, e),
@@ -466,6 +498,7 @@ function A$1(e) {
466
498
  archive: (e) => O$1(A.flag.archive.$post, e),
467
499
  listArchived: (e) => O$1(A.flag.listArchived.$post, e),
468
500
  restore: (e) => O$1(A.flag.restore.$post, e),
501
+ publishEnvironments: (e) => O$1(A.flag.publishEnvironments.$post, e),
469
502
  updateEnvironment: (e) => O$1(A.flag.updateEnvironment.$post, e),
470
503
  toggleEnvironment: (e) => O$1(A.flag.toggleEnvironment.$post, e),
471
504
  listEnvironmentStates: (e) => O$1(A.flag.listEnvironmentStates.$post, e),
@@ -608,7 +641,7 @@ function A$1(e) {
608
641
  }
609
642
 
610
643
  //#endregion
611
- //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_e02eb0bf6cc57b8ec4097db90c5ddf37/node_modules/@better-auth/core/dist/utils/error-codes.mjs
644
+ //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_0d5083f53bdf976e0320d8b98ec7aa98/node_modules/@better-auth/core/dist/utils/error-codes.mjs
612
645
  function defineErrorCodes(codes) {
613
646
  return Object.fromEntries(Object.entries(codes).map(([key, value]) => [key, {
614
647
  code: key,
@@ -618,7 +651,7 @@ function defineErrorCodes(codes) {
618
651
  }
619
652
 
620
653
  //#endregion
621
- //#region ../../node_modules/.pnpm/better-call@1.4.0_zod@4.6.5/node_modules/better-call/dist/error.mjs
654
+ //#region ../../node_modules/.pnpm/better-call@1.4.0_zod@4.6.7/node_modules/better-call/dist/error.mjs
622
655
  function isErrorStackTraceLimitWritable() {
623
656
  const desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit");
624
657
  if (desc === void 0) return Object.isExtensible(Error);
@@ -735,7 +768,7 @@ var InternalAPIError = class extends Error {
735
768
  const APIError = makeErrorForHideStackFrame(InternalAPIError, Error);
736
769
 
737
770
  //#endregion
738
- //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_e02eb0bf6cc57b8ec4097db90c5ddf37/node_modules/@better-auth/core/dist/error/index.mjs
771
+ //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_0d5083f53bdf976e0320d8b98ec7aa98/node_modules/@better-auth/core/dist/error/index.mjs
739
772
  var BetterAuthError = class extends Error {
740
773
  constructor(message, options) {
741
774
  super(message, options);
@@ -746,7 +779,7 @@ var BetterAuthError = class extends Error {
746
779
  };
747
780
 
748
781
  //#endregion
749
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/access/access.mjs
782
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/access/access.mjs
750
783
  function unknownResourceResponse(requestedResource) {
751
784
  return {
752
785
  success: false,
@@ -823,7 +856,7 @@ function createAccessControl(s) {
823
856
  }
824
857
 
825
858
  //#endregion
826
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/two-factor/error-code.mjs
859
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/two-factor/error-code.mjs
827
860
  const TWO_FACTOR_ERROR_CODES = defineErrorCodes({
828
861
  OTP_NOT_ENABLED: "OTP not enabled",
829
862
  OTP_NOT_CONFIGURED: "OTP is not available",
@@ -841,15 +874,15 @@ const TWO_FACTOR_ERROR_CODES = defineErrorCodes({
841
874
  });
842
875
 
843
876
  //#endregion
844
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/package.mjs
877
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/package.mjs
845
878
  var version = "1.7.7";
846
879
 
847
880
  //#endregion
848
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/version.mjs
881
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/version.mjs
849
882
  const PACKAGE_VERSION$3 = version;
850
883
 
851
884
  //#endregion
852
- //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_e02eb0bf6cc57b8ec4097db90c5ddf37/node_modules/@better-auth/core/dist/utils/url.mjs
885
+ //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_0d5083f53bdf976e0320d8b98ec7aa98/node_modules/@better-auth/core/dist/utils/url.mjs
853
886
  /**
854
887
  * Schemes that execute or embed code when navigated to or accepted as a
855
888
  * redirect target. These are never safe as an OAuth `redirect_uri` or as a
@@ -881,7 +914,7 @@ function isSafeUrlScheme(value) {
881
914
  }
882
915
 
883
916
  //#endregion
884
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/two-factor/client.mjs
917
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/two-factor/client.mjs
885
918
  const twoFactorClient = (options) => {
886
919
  return {
887
920
  id: "two-factor",
@@ -919,7 +952,7 @@ const twoFactorClient = (options) => {
919
952
  };
920
953
 
921
954
  //#endregion
922
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/admin/access/statement.mjs
955
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/admin/access/statement.mjs
923
956
  const defaultStatements$1 = {
924
957
  user: [
925
958
  "create",
@@ -970,7 +1003,7 @@ const defaultRoles$1 = {
970
1003
  };
971
1004
 
972
1005
  //#endregion
973
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/admin/error-codes.mjs
1006
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/admin/error-codes.mjs
974
1007
  const ADMIN_ERROR_CODES = defineErrorCodes({
975
1008
  FAILED_TO_CREATE_USER: "Failed to create user",
976
1009
  USER_ALREADY_EXISTS: "User already exists.",
@@ -998,7 +1031,7 @@ const ADMIN_ERROR_CODES = defineErrorCodes({
998
1031
  });
999
1032
 
1000
1033
  //#endregion
1001
- //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_e02eb0bf6cc57b8ec4097db90c5ddf37/node_modules/@better-auth/core/dist/env/env-impl.mjs
1034
+ //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_0d5083f53bdf976e0320d8b98ec7aa98/node_modules/@better-auth/core/dist/env/env-impl.mjs
1002
1035
  const _envShim = Object.create(null);
1003
1036
  const _getEnv = (useShim) => globalThis.process?.env || globalThis.Deno?.env.toObject() || globalThis.__env__ || (useShim ? _envShim : globalThis);
1004
1037
  const env = new Proxy(_envShim, {
@@ -1062,7 +1095,7 @@ const ENV = Object.freeze({
1062
1095
  });
1063
1096
 
1064
1097
  //#endregion
1065
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/utils/url.mjs
1098
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/utils/url.mjs
1066
1099
  const SLASH_CHAR_CODE = "/".charCodeAt(0);
1067
1100
  function trimTrailingSlashes(value) {
1068
1101
  let end = value.length;
@@ -1141,7 +1174,7 @@ function getOrigin(url) {
1141
1174
  }
1142
1175
 
1143
1176
  //#endregion
1144
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/admin/has-permission.mjs
1177
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/admin/has-permission.mjs
1145
1178
  const hasPermission = (input) => {
1146
1179
  if (input.userId && input.options?.adminUserIds?.includes(input.userId)) return true;
1147
1180
  if (!input.permissions) return false;
@@ -1743,7 +1776,7 @@ const createFetch = (config) => {
1743
1776
  };
1744
1777
 
1745
1778
  //#endregion
1746
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/parser.mjs
1779
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/parser.mjs
1747
1780
  const PROTO_POLLUTION_PATTERNS = {
1748
1781
  proto: /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/,
1749
1782
  constructor: /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/,
@@ -1813,7 +1846,7 @@ function parseJSON(value, options = { strict: true }) {
1813
1846
  }
1814
1847
 
1815
1848
  //#endregion
1816
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/organization/access/statement.mjs
1849
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/organization/access/statement.mjs
1817
1850
  const defaultStatements = {
1818
1851
  organization: ["update", "delete"],
1819
1852
  member: [
@@ -1889,7 +1922,7 @@ const defaultRoles = {
1889
1922
  };
1890
1923
 
1891
1924
  //#endregion
1892
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/organization/permission.mjs
1925
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/organization/permission.mjs
1893
1926
  const hasPermissionFn = (input, acRoles) => {
1894
1927
  if (!input.permissions) return false;
1895
1928
  const roles = input.role.split(",");
@@ -1902,7 +1935,7 @@ const hasPermissionFn = (input, acRoles) => {
1902
1935
  };
1903
1936
 
1904
1937
  //#endregion
1905
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/organization/error-codes.mjs
1938
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/organization/error-codes.mjs
1906
1939
  const ORGANIZATION_ERROR_CODES = defineErrorCodes({
1907
1940
  YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION: "You are not allowed to create a new organization",
1908
1941
  YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS: "You have reached the maximum number of organizations",
@@ -2240,7 +2273,7 @@ const n$1 = {
2240
2273
  };
2241
2274
 
2242
2275
  //#endregion
2243
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/broadcast-channel.mjs
2276
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/broadcast-channel.mjs
2244
2277
  const kBroadcastChannel = Symbol.for("better-auth:broadcast-channel");
2245
2278
  const now$1 = () => Math.floor(Date.now() / 1e3);
2246
2279
  var WindowBroadcastChannel = class {
@@ -2464,7 +2497,7 @@ let onMount = ($store, initialize) => {
2464
2497
  };
2465
2498
 
2466
2499
  //#endregion
2467
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/equality.mjs
2500
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/equality.mjs
2468
2501
  function isPlainObject(value) {
2469
2502
  if (typeof value !== "object" || value === null) return false;
2470
2503
  const prototype = Object.getPrototypeOf(value);
@@ -2505,7 +2538,7 @@ function withEquality(store, isEqual) {
2505
2538
  }
2506
2539
 
2507
2540
  //#endregion
2508
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/focus-manager.mjs
2541
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/focus-manager.mjs
2509
2542
  const kFocusManager = Symbol.for("better-auth:focus-manager");
2510
2543
  var WindowFocusManager = class {
2511
2544
  listeners = /* @__PURE__ */ new Set();
@@ -2535,7 +2568,7 @@ function getGlobalFocusManager() {
2535
2568
  }
2536
2569
 
2537
2570
  //#endregion
2538
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/online-manager.mjs
2571
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/online-manager.mjs
2539
2572
  const kOnlineManager = Symbol.for("better-auth:online-manager");
2540
2573
  var WindowOnlineManager = class {
2541
2574
  listeners = /* @__PURE__ */ new Set();
@@ -2568,7 +2601,7 @@ function getGlobalOnlineManager() {
2568
2601
  }
2569
2602
 
2570
2603
  //#endregion
2571
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/query-atom.mjs
2604
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/query-atom.mjs
2572
2605
  const kAuthQuery = Symbol.for("better-auth:auth-query");
2573
2606
  function createAuthQueryAtom(initialValue) {
2574
2607
  const queryAtom = /* @__PURE__ */ atom(initialValue);
@@ -2577,7 +2610,7 @@ function createAuthQueryAtom(initialValue) {
2577
2610
  }
2578
2611
 
2579
2612
  //#endregion
2580
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/query.mjs
2613
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/query.mjs
2581
2614
  const isServer$1 = () => typeof window === "undefined";
2582
2615
  function isAuthQueryStateEqual(a, b) {
2583
2616
  return isJsonEqual(a.data, b.data) && a.error === b.error && a.isPending === b.isPending && a.isRefetching === b.isRefetching && a.refetch === b.refetch;
@@ -2708,7 +2741,7 @@ const useAuthQuery = (initializedAtom, path, $fetch, options) => {
2708
2741
  };
2709
2742
 
2710
2743
  //#endregion
2711
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/session-refresh.mjs
2744
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/session-refresh.mjs
2712
2745
  const now = () => Math.floor(Date.now() / 1e3);
2713
2746
  /**
2714
2747
  * Rate limit: don't refetch on focus if a session request was made within this many seconds
@@ -2836,7 +2869,7 @@ function createSessionRefreshManager(opts) {
2836
2869
  }
2837
2870
 
2838
2871
  //#endregion
2839
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/fetch-plugins.mjs
2872
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/fetch-plugins.mjs
2840
2873
  const redirectPlugin = {
2841
2874
  id: "redirect",
2842
2875
  name: "Redirect",
@@ -2852,7 +2885,7 @@ const redirectPlugin = {
2852
2885
  };
2853
2886
 
2854
2887
  //#endregion
2855
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/session-atom.mjs
2888
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/session-atom.mjs
2856
2889
  const isServer = () => typeof window === "undefined";
2857
2890
  const SESSION_MOUNT_DEDUPE_INTERVAL = STORE_UNMOUNT_DELAY;
2858
2891
  function hydrateSessionAtom(sessionAtom, session) {
@@ -3027,7 +3060,7 @@ function getSessionAtom($fetch, options) {
3027
3060
  }
3028
3061
 
3029
3062
  //#endregion
3030
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/config.mjs
3063
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/config.mjs
3031
3064
  const resolvePublicAuthUrl = (basePath) => {
3032
3065
  if (typeof process === "undefined") return void 0;
3033
3066
  const path = basePath ?? "/api/auth";
@@ -3133,13 +3166,13 @@ const getClientConfig = (options, loadEnv) => {
3133
3166
  };
3134
3167
 
3135
3168
  //#endregion
3136
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/utils/is-atom.mjs
3169
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/utils/is-atom.mjs
3137
3170
  function isAtom(value) {
3138
3171
  return typeof value === "object" && value !== null && "get" in value && typeof value.get === "function" && "lc" in value && typeof value.lc === "number";
3139
3172
  }
3140
3173
 
3141
3174
  //#endregion
3142
- //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_e02eb0bf6cc57b8ec4097db90c5ddf37/node_modules/@better-auth/core/dist/utils/string.mjs
3175
+ //#region ../../node_modules/.pnpm/@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.2_@opentelemet_0d5083f53bdf976e0320d8b98ec7aa98/node_modules/@better-auth/core/dist/utils/string.mjs
3143
3176
  function capitalizeFirstLetter(str) {
3144
3177
  return str.charAt(0).toUpperCase() + str.slice(1);
3145
3178
  }
@@ -3153,7 +3186,7 @@ function toKebabCase(input) {
3153
3186
  }
3154
3187
 
3155
3188
  //#endregion
3156
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/proxy.mjs
3189
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/proxy.mjs
3157
3190
  function getMethod(path, knownPathMethods, args) {
3158
3191
  const method = knownPathMethods[path];
3159
3192
  const { fetchOptions, query: _query, ...body } = args || {};
@@ -3229,7 +3262,7 @@ function createDynamicPathProxy(routes, client, knownPathMethods, atoms, atomLis
3229
3262
  }
3230
3263
 
3231
3264
  //#endregion
3232
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/client/vanilla.mjs
3265
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/client/vanilla.mjs
3233
3266
  function createAuthClient(options) {
3234
3267
  const { pluginPathMethods, pluginsActions, pluginsAtoms, hydrateSession, $fetch, atomListeners, $store } = getClientConfig(options);
3235
3268
  const resolvedHooks = {};
@@ -3244,7 +3277,7 @@ function createAuthClient(options) {
3244
3277
  }
3245
3278
 
3246
3279
  //#endregion
3247
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/additional-fields/client.mjs
3280
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/additional-fields/client.mjs
3248
3281
  const inferAdditionalFields = (schema) => {
3249
3282
  return {
3250
3283
  id: "additional-fields-client",
@@ -3254,7 +3287,7 @@ const inferAdditionalFields = (schema) => {
3254
3287
  };
3255
3288
 
3256
3289
  //#endregion
3257
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/admin/client.mjs
3290
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/admin/client.mjs
3258
3291
  const adminClient = (options) => {
3259
3292
  const roles = {
3260
3293
  admin: adminAc$1,
@@ -3289,7 +3322,7 @@ const adminClient = (options) => {
3289
3322
  };
3290
3323
 
3291
3324
  //#endregion
3292
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/device-authorization/client.mjs
3325
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/device-authorization/client.mjs
3293
3326
  const deviceAuthorizationClient = () => {
3294
3327
  return {
3295
3328
  id: "device-authorization",
@@ -3306,7 +3339,7 @@ const deviceAuthorizationClient = () => {
3306
3339
  };
3307
3340
 
3308
3341
  //#endregion
3309
- //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._025f420fe344f13948728b361b367060/node_modules/better-auth/dist/plugins/organization/client.mjs
3342
+ //#region ../../node_modules/.pnpm/better-auth@1.7.7_@opentelemetry+api@1.9.1_drizzle-kit@1.0.0-rc.4_drizzle-orm@1.0.0-rc._7c90dee8a2acd893f91862de908c6808/node_modules/better-auth/dist/plugins/organization/client.mjs
3310
3343
  /**
3311
3344
  * Using the same `hasPermissionFn` function, but without the need for a `ctx` parameter or the `organizationId` parameter.
3312
3345
  */
@@ -3401,7 +3434,7 @@ const organizationClient = (options) => {
3401
3434
  };
3402
3435
 
3403
3436
  //#endregion
3404
- //#region ../../node_modules/.pnpm/@better-auth+api-key@1.7.7_@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fet_1231755eab9fee363c80bc8eefaed94a/node_modules/@better-auth/api-key/dist/version-CUtTQbZf.mjs
3437
+ //#region ../../node_modules/.pnpm/@better-auth+api-key@1.7.7_@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fet_3f872e6aea52bc61f9c5dfb40b2109fb/node_modules/@better-auth/api-key/dist/version-CUtTQbZf.mjs
3405
3438
  const API_KEY_ERROR_CODES = defineErrorCodes({
3406
3439
  INVALID_METADATA_TYPE: "metadata must be an object or undefined",
3407
3440
  REFILL_AMOUNT_AND_INTERVAL_REQUIRED: "refillAmount is required when refillInterval is provided",
@@ -3438,7 +3471,7 @@ const API_KEY_ERROR_CODES = defineErrorCodes({
3438
3471
  const PACKAGE_VERSION$2 = "1.7.7";
3439
3472
 
3440
3473
  //#endregion
3441
- //#region ../../node_modules/.pnpm/@better-auth+api-key@1.7.7_@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fet_1231755eab9fee363c80bc8eefaed94a/node_modules/@better-auth/api-key/dist/client.mjs
3474
+ //#region ../../node_modules/.pnpm/@better-auth+api-key@1.7.7_@better-auth+core@1.7.7_@better-auth+utils@0.4.2_@better-fet_3f872e6aea52bc61f9c5dfb40b2109fb/node_modules/@better-auth/api-key/dist/client.mjs
3442
3475
  const apiKeyClient = () => {
3443
3476
  return {
3444
3477
  id: "api-key",
@@ -3454,7 +3487,7 @@ const apiKeyClient = () => {
3454
3487
  };
3455
3488
 
3456
3489
  //#endregion
3457
- //#region ../../node_modules/.pnpm/@better-auth+passkey@1.7.7_8a2847836f4e26061e61f8849e215b9d/node_modules/@better-auth/passkey/dist/version-Bx477_EO.mjs
3490
+ //#region ../../node_modules/.pnpm/@better-auth+passkey@1.7.7_59c4b159f325028b6aeb6ca5fe859369/node_modules/@better-auth/passkey/dist/version-Bx477_EO.mjs
3458
3491
  const PASSKEY_ERROR_CODES = defineErrorCodes({
3459
3492
  CHALLENGE_NOT_FOUND: "Challenge not found",
3460
3493
  YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY: "You are not allowed to register this passkey",
@@ -3944,7 +3977,7 @@ async function startAuthentication(options) {
3944
3977
  }
3945
3978
 
3946
3979
  //#endregion
3947
- //#region ../../node_modules/.pnpm/@better-auth+passkey@1.7.7_8a2847836f4e26061e61f8849e215b9d/node_modules/@better-auth/passkey/dist/client.mjs
3980
+ //#region ../../node_modules/.pnpm/@better-auth+passkey@1.7.7_59c4b159f325028b6aeb6ca5fe859369/node_modules/@better-auth/passkey/dist/client.mjs
3948
3981
  const getPasskeyActions = ($fetch, { $listPasskeys, $store }) => {
3949
3982
  const signInPasskey = async (opts, options) => {
3950
3983
  const response = await $fetch("/passkey/generate-authenticate-options", {
@@ -4146,11 +4179,11 @@ const passkeyClient = () => {
4146
4179
  };
4147
4180
 
4148
4181
  //#endregion
4149
- //#region ../../node_modules/.pnpm/@better-auth+sso@1.7.7_9007e608a51715e1e8e182bfada736ab/node_modules/@better-auth/sso/dist/version-DFla2W8i.mjs
4182
+ //#region ../../node_modules/.pnpm/@better-auth+sso@1.7.7_f840261b078ff9b2a25d6de6ccbfccc0/node_modules/@better-auth/sso/dist/version-DFla2W8i.mjs
4150
4183
  const PACKAGE_VERSION = "1.7.7";
4151
4184
 
4152
4185
  //#endregion
4153
- //#region ../../node_modules/.pnpm/@better-auth+sso@1.7.7_9007e608a51715e1e8e182bfada736ab/node_modules/@better-auth/sso/dist/client.mjs
4186
+ //#region ../../node_modules/.pnpm/@better-auth+sso@1.7.7_f840261b078ff9b2a25d6de6ccbfccc0/node_modules/@better-auth/sso/dist/client.mjs
4154
4187
  const ssoClient = (options) => {
4155
4188
  return {
4156
4189
  id: "sso-client",
@@ -4200,25 +4233,6 @@ const f$1 = (e) => createAuthClient({
4200
4233
  plugins: d$1(e)
4201
4234
  });
4202
4235
 
4203
- //#endregion
4204
- //#region src/lib/errors.ts
4205
- var CliError = class extends Error {
4206
- code;
4207
- exitCode;
4208
- details;
4209
- constructor(code, message, options) {
4210
- super(message);
4211
- this.name = "CliError";
4212
- this.code = code;
4213
- this.exitCode = options?.exitCode ?? 1;
4214
- this.details = options?.details;
4215
- }
4216
- };
4217
- function exitCodeForError(error) {
4218
- if (error instanceof CliError) return error.exitCode;
4219
- return 1;
4220
- }
4221
-
4222
4236
  //#endregion
4223
4237
  //#region src/lib/client.ts
4224
4238
  const AUTH_BASE_PATH = "/api/v1/auth";
@@ -4621,6 +4635,7 @@ const w = z.object({
4621
4635
  });
4622
4636
  });
4623
4637
  const T = z.object({
4638
+ expectedVersion: z.number().int().positive().optional(),
4624
4639
  flagId: z.string().min(1),
4625
4640
  key: o.optional(),
4626
4641
  name: z.string().min(1).optional(),
@@ -4633,6 +4648,7 @@ const E = z.object({ flagId: z.string().min(1) });
4633
4648
  const D = z.object({ projectId: z.string().min(1) });
4634
4649
  const O = z.object({ flagId: z.string().min(1) });
4635
4650
  const k = z.object({
4651
+ expectedVersion: z.number().int().positive().optional(),
4636
4652
  flagId: z.string().min(1),
4637
4653
  environmentId: z.string().min(1),
4638
4654
  enabled: z.boolean().optional(),
@@ -4640,17 +4656,25 @@ const k = z.object({
4640
4656
  fallthrough: u.optional()
4641
4657
  });
4642
4658
  const A = z.object({
4659
+ flagId: z.string().min(1),
4660
+ environments: z.array(z.object({
4661
+ environmentId: z.string().min(1),
4662
+ expectedVersion: z.number().int().positive()
4663
+ })).min(1).refine((e) => new Set(e.map((e) => e.environmentId)).size === e.length, `Duplicate environments`)
4664
+ });
4665
+ const j = z.object({
4666
+ expectedVersion: z.number().int().positive().optional(),
4643
4667
  flagId: z.string().min(1),
4644
4668
  environmentId: z.string().min(1),
4645
4669
  enabled: z.boolean()
4646
4670
  });
4647
- const j = z.object({ flagId: z.string().min(1) });
4648
- const M = z.enum([`active`, `deprecated`]);
4649
- function N(e) {
4671
+ const M = z.object({ flagId: z.string().min(1) });
4672
+ const N = z.enum([`active`, `deprecated`]);
4673
+ function P(e) {
4650
4674
  let t = Object.getPrototypeOf(e);
4651
4675
  return t === Object.prototype || t === null;
4652
4676
  }
4653
- function P(e, t) {
4677
+ function F(e, t) {
4654
4678
  if (e === null) return !0;
4655
4679
  switch (typeof e) {
4656
4680
  case `boolean`:
@@ -4658,13 +4682,13 @@ function P(e, t) {
4658
4682
  case `number`: return Number.isFinite(e);
4659
4683
  case `object`: {
4660
4684
  let n = t ?? /* @__PURE__ */ new WeakSet();
4661
- return !n.has(e) && (n.add(e), Array.isArray(e) ? e.every((e) => P(e, n)) : !N(e) || Object.getOwnPropertySymbols(e).length > 0 ? !1 : Object.keys(e).every((t) => P(e[t], n)));
4685
+ return !n.has(e) && (n.add(e), Array.isArray(e) ? e.every((e) => F(e, n)) : !P(e) || Object.getOwnPropertySymbols(e).length > 0 ? !1 : Object.keys(e).every((t) => F(e[t], n)));
4662
4686
  }
4663
4687
  default: return !1;
4664
4688
  }
4665
4689
  }
4666
- const F = z.custom(P, { message: `default must be JSON-serializable` });
4667
- function I(e, t) {
4690
+ const I = z.custom(F, { message: `default must be JSON-serializable` });
4691
+ function L(e, t) {
4668
4692
  switch (e.type) {
4669
4693
  case `boolean`:
4670
4694
  typeof e.default != `boolean` && t.addIssue({
@@ -4693,43 +4717,43 @@ function I(e, t) {
4693
4717
  });
4694
4718
  break;
4695
4719
  }
4696
- case `json`: F.safeParse(e.default).success || t.addIssue({
4720
+ case `json`: I.safeParse(e.default).success || t.addIssue({
4697
4721
  code: `custom`,
4698
4722
  path: [`default`],
4699
4723
  message: `json flag default must be JSON-serializable`
4700
4724
  });
4701
4725
  }
4702
4726
  }
4703
- const L = z.object({
4727
+ const R = z.object({
4704
4728
  key: o,
4705
4729
  type: t,
4706
4730
  default: z.unknown(),
4707
4731
  description: z.string().optional(),
4708
4732
  tags: z.array(z.string()).default([]),
4709
- lifecycle: M.default(`active`),
4733
+ lifecycle: N.default(`active`),
4710
4734
  variants: z.array(z.string()).optional(),
4711
4735
  disabled: z.boolean().optional()
4712
4736
  });
4713
- const R = L.superRefine(I);
4714
- const z$1 = L.extend({
4737
+ const z$1 = R.superRefine(L);
4738
+ const B = R.extend({
4715
4739
  version: z.number().int().positive(),
4716
4740
  environmentVersion: z.number().int().positive().optional(),
4717
4741
  managedBy: n
4718
- }).superRefine(I);
4719
- const B = z.object({
4742
+ }).superRefine(L);
4743
+ const V = z.object({
4720
4744
  version: z.number().int().positive(),
4721
4745
  environmentVersion: z.number().int().positive().optional()
4722
4746
  });
4723
- const V = z.object({
4747
+ const H = z.object({
4724
4748
  projectId: z.string().min(1),
4725
4749
  environmentId: z.string().min(1)
4726
4750
  });
4727
- const H = z.object({
4751
+ const U = z.object({
4728
4752
  projectId: z.string(),
4729
4753
  environmentId: z.string(),
4730
- flags: z.array(z$1)
4754
+ flags: z.array(B)
4731
4755
  });
4732
- function U(e, t) {
4756
+ function W(e, t) {
4733
4757
  let n = /* @__PURE__ */ new Set();
4734
4758
  for (let [r, i] of e.entries()) n.has(i.key) && t.addIssue({
4735
4759
  code: `custom`,
@@ -4741,12 +4765,12 @@ function U(e, t) {
4741
4765
  message: `duplicate flag key "${i.key}"`
4742
4766
  }), n.add(i.key);
4743
4767
  }
4744
- const W = z.object({
4768
+ const G = z.object({
4745
4769
  projectId: z.string().min(1),
4746
4770
  environmentId: z.string().min(1),
4747
- flags: z.array(R).max(500)
4748
- }).superRefine((e, t) => U(e.flags, t));
4749
- const G = z.object({
4771
+ flags: z.array(z$1).max(500)
4772
+ }).superRefine((e, t) => W(e.flags, t));
4773
+ const K = z.object({
4750
4774
  key: z.string(),
4751
4775
  op: z.enum([
4752
4776
  `add`,
@@ -4754,40 +4778,223 @@ const G = z.object({
4754
4778
  `remove`,
4755
4779
  `unlinked`
4756
4780
  ]),
4757
- before: z$1.optional(),
4758
- after: R.optional(),
4781
+ before: B.optional(),
4782
+ after: z$1.optional(),
4759
4783
  message: z.string().optional()
4760
4784
  });
4761
- const K = z.object({
4785
+ const q = z.object({
4762
4786
  projectId: z.string(),
4763
4787
  environmentId: z.string(),
4764
- changes: z.array(G),
4765
- remoteOnly: z.array(z$1).default([])
4788
+ changes: z.array(K),
4789
+ remoteOnly: z.array(B).default([])
4766
4790
  });
4767
- const q = z.object({
4791
+ const J = z.object({
4768
4792
  projectId: z.string().min(1),
4769
4793
  environmentId: z.string().min(1),
4770
- flags: z.array(R).max(500),
4794
+ flags: z.array(z$1).max(500),
4771
4795
  apply: z.boolean().default(!1),
4772
4796
  prune: z.boolean().default(!1),
4773
4797
  force: z.boolean().default(!1),
4774
4798
  adopt: z.boolean().default(!1),
4775
- expectedVersions: z.record(z.string(), B).optional()
4776
- }).superRefine((e, t) => U(e.flags, t));
4777
- const J = z.object({
4799
+ expectedVersions: z.record(z.string(), V).optional()
4800
+ }).superRefine((e, t) => W(e.flags, t));
4801
+ const Y = z.object({
4778
4802
  projectId: z.string(),
4779
4803
  environmentId: z.string(),
4780
4804
  applied: z.boolean(),
4781
- changes: z.array(G),
4782
- remoteOnly: z.array(z$1).default([]),
4805
+ changes: z.array(K),
4806
+ remoteOnly: z.array(B).default([]),
4783
4807
  forced: z.boolean().default(!1),
4784
4808
  adopted: z.boolean().default(!1),
4785
- approvalRequests: z.array(z.string()).default([])
4809
+ approvalRequests: z.array(z.string()).default([]),
4810
+ drafts: z.array(z.string()).default([])
4786
4811
  });
4787
4812
 
4813
+ //#endregion
4814
+ //#region src/lib/go-string.ts
4815
+ /** Go strings cannot represent JavaScript's unpaired UTF-16 surrogates. */
4816
+ function goString(value) {
4817
+ for (const character of value) {
4818
+ const code = character.charCodeAt(0);
4819
+ if (character.length === 1 && code >= 55296 && code <= 57343) throw new CliError("validation_error", `Cannot generate Go for a string containing an unpaired Unicode surrogate: ${JSON.stringify(value)}`);
4820
+ }
4821
+ return JSON.stringify(value);
4822
+ }
4823
+
4824
+ //#endregion
4825
+ //#region src/lib/go-config.ts
4826
+ /** Read the package clause after leading whitespace and comments, not comment text. */
4827
+ function goPackageClause(source) {
4828
+ let offset = 0;
4829
+ const skipTrivia = () => {
4830
+ while (offset < source.length) {
4831
+ const trivia = /^(?:\s+|\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)/u.exec(source.slice(offset));
4832
+ if (!trivia) break;
4833
+ offset += trivia[0].length;
4834
+ }
4835
+ };
4836
+ skipTrivia();
4837
+ if (!/^package\b/u.test(source.slice(offset))) throw new Error("Go config must have a package declaration");
4838
+ offset += 7;
4839
+ skipTrivia();
4840
+ const name = /^[_\p{L}][_\p{L}\p{Nd}]*/u.exec(source.slice(offset))?.[0];
4841
+ if (!name) throw new Error("Go config must have a package name");
4842
+ return {
4843
+ name,
4844
+ start: offset,
4845
+ end: offset + name.length
4846
+ };
4847
+ }
4848
+ /** Evaluate a self-contained Go definition file within its own module. */
4849
+ async function loadGoConfig(file) {
4850
+ const source = fs.readFileSync(file, "utf8");
4851
+ const clause = goPackageClause(source);
4852
+ const directory = fs.mkdtempSync(path.join(path.dirname(file), ".shipd-go-"));
4853
+ const cleanup = () => fs.rmSync(directory, {
4854
+ recursive: true,
4855
+ force: true
4856
+ });
4857
+ process.once("exit", cleanup);
4858
+ let interrupted;
4859
+ let terminate;
4860
+ const interrupt = () => {
4861
+ interrupted = 130;
4862
+ terminate?.();
4863
+ };
4864
+ const shutdown = () => {
4865
+ interrupted = 143;
4866
+ terminate?.();
4867
+ };
4868
+ process.once("SIGINT", interrupt);
4869
+ process.once("SIGTERM", shutdown);
4870
+ try {
4871
+ fs.writeFileSync(path.join(directory, ".gitignore"), "*\n");
4872
+ fs.writeFileSync(path.join(directory, "definitions.go"), source.slice(0, clause.start) + "main" + source.slice(clause.end));
4873
+ fs.writeFileSync(path.join(directory, "main.go"), `package main
4874
+ import (
4875
+ "encoding/json"
4876
+ "fmt"
4877
+ "os"
4878
+ )
4879
+ func main() {
4880
+ file, err := os.Create(os.Args[1])
4881
+ if err == nil {
4882
+ err = json.NewEncoder(file).Encode(Config)
4883
+ if closeErr := file.Close(); err == nil { err = closeErr }
4884
+ }
4885
+ if err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
4886
+ }
4887
+ `);
4888
+ const output = path.join(directory, "config.json");
4889
+ try {
4890
+ await new Promise((resolve, reject) => {
4891
+ const grouped = process.platform !== "win32";
4892
+ const child = spawn("go", [
4893
+ "run",
4894
+ ".",
4895
+ output
4896
+ ], {
4897
+ cwd: directory,
4898
+ detached: grouped,
4899
+ stdio: [
4900
+ "ignore",
4901
+ "ignore",
4902
+ "pipe"
4903
+ ]
4904
+ });
4905
+ let stderr = "";
4906
+ let failure;
4907
+ const kill = (signal) => {
4908
+ if (!child.pid) return;
4909
+ try {
4910
+ if (grouped) process.kill(-child.pid, signal);
4911
+ else child.kill(signal);
4912
+ } catch (error) {
4913
+ if (error.code !== "ESRCH") throw error;
4914
+ }
4915
+ };
4916
+ let killTimer;
4917
+ terminate = () => {
4918
+ kill("SIGTERM");
4919
+ killTimer = setTimeout(() => kill("SIGKILL"), 1e3);
4920
+ };
4921
+ const timer = setTimeout(() => {
4922
+ failure = /* @__PURE__ */ new Error("Go config evaluation timed out");
4923
+ kill("SIGKILL");
4924
+ }, 12e4);
4925
+ child.stderr.on("data", (chunk) => {
4926
+ stderr += chunk.toString();
4927
+ if (stderr.length > 8388608) {
4928
+ failure = /* @__PURE__ */ new Error("Go compiler output exceeds 8 MiB");
4929
+ kill("SIGKILL");
4930
+ }
4931
+ });
4932
+ child.once("error", (error) => {
4933
+ failure = error;
4934
+ });
4935
+ child.once("close", (code) => {
4936
+ clearTimeout(timer);
4937
+ clearTimeout(killTimer);
4938
+ terminate = void 0;
4939
+ if (failure || code !== 0) reject(Object.assign(failure ?? /* @__PURE__ */ new Error(`go run exited with code ${code}`), { stderr }));
4940
+ else resolve();
4941
+ });
4942
+ });
4943
+ } catch (error) {
4944
+ throw goRunError(error, path.basename(file));
4945
+ }
4946
+ return JSON.parse(fs.readFileSync(output, "utf8"));
4947
+ } finally {
4948
+ process.removeListener("SIGINT", interrupt);
4949
+ process.removeListener("SIGTERM", shutdown);
4950
+ process.removeListener("exit", cleanup);
4951
+ cleanup();
4952
+ if (interrupted !== void 0) process.exit(interrupted);
4953
+ }
4954
+ }
4955
+ function goRunError(error, configName) {
4956
+ if (error.code === "ENOENT") return /* @__PURE__ */ new Error("Go 1.23+ is required to load Go flag definitions, but `go` was not found on PATH. Install Go from https://go.dev/dl and try again.");
4957
+ const stderr = String(error.stderr ?? "").replace(/(?:\.\/)?definitions\.go/g, configName).trim();
4958
+ const hint = /\bundefined:/.test(stderr) ? `\n${configName} must be self-contained: the CLI compiles only this file, not sibling Go files. Move helper declarations into it or import them from another package.` : "";
4959
+ return new Error(stderr ? `go run failed:\n${stderr}${hint}` : String(error));
4960
+ }
4961
+ function goNumber(value) {
4962
+ const literal = JSON.stringify(value);
4963
+ return Number.isSafeInteger(value) || /[.eE]/.test(literal) ? literal : `${literal}.0`;
4964
+ }
4965
+ function goValue(value) {
4966
+ if (value === null) return "nil";
4967
+ if (typeof value === "number") return goNumber(value);
4968
+ if (typeof value === "string") return goString(value);
4969
+ if (Array.isArray(value)) return `[]any{${value.map(goValue).join(", ")}}`;
4970
+ if (typeof value === "object") return `map[string]any{${Object.entries(value).map(([key, entry]) => `${goString(key)}: ${goValue(entry)}`).join(", ")}}`;
4971
+ return JSON.stringify(value);
4972
+ }
4973
+ /** Emits gofmt-clean Go: one block per flag, field values aligned as gofmt does. */
4974
+ function generateGoConfigSource(flags, packageName = "main") {
4975
+ const entries = flags.map((flag) => {
4976
+ const fields = [["Type", goString(flag.type)], ["Default", goValue(flag.default)]];
4977
+ if (flag.variants) fields.push(["Variants", `[]string{${flag.variants.map((v) => goString(v)).join(", ")}}`]);
4978
+ if (flag.description !== void 0) fields.push(["Description", goString(flag.description)]);
4979
+ if (flag.tags.length) fields.push(["Tags", `[]string{${flag.tags.map((v) => goString(v)).join(", ")}}`]);
4980
+ if (flag.lifecycle === "deprecated") fields.push(["Deprecated", "true"]);
4981
+ if (flag.disabled) fields.push(["Disabled", "true"]);
4982
+ const width = Math.max(...fields.map(([name]) => name.length)) + 1;
4983
+ const lines = fields.map(([name, value]) => `\t\t${`${name}:`.padEnd(width)} ${value},`);
4984
+ return `\t${goString(flag.key)}: {\n${lines.join("\n")}\n\t},`;
4985
+ });
4986
+ return `// Generated by shipd pull. Pull replaces this file; review changes before committing.
4987
+ package ${packageName}
4988
+
4989
+ import "github.com/alasti-company/useshipd.com/sdks/go/config"
4990
+
4991
+ var Config = config.ShipdConfig${entries.length ? `{\n${entries.join("\n")}\n}` : "{}"}
4992
+ `;
4993
+ }
4994
+
4788
4995
  //#endregion
4789
4996
  //#region src/lib/local-config.ts
4790
- const localFlagsArraySchema = z.array(R).superRefine((flags, ctx) => {
4997
+ const localFlagsArraySchema = z.array(z$1).superRefine((flags, ctx) => {
4791
4998
  const seen = /* @__PURE__ */ new Set();
4792
4999
  for (const [index, flag] of flags.entries()) {
4793
5000
  if (seen.has(flag.key)) ctx.addIssue({
@@ -4801,23 +5008,32 @@ const localFlagsArraySchema = z.array(R).superRefine((flags, ctx) => {
4801
5008
  async function loadLocalFlags(configFile) {
4802
5009
  const absolute = path.resolve(configFile);
4803
5010
  if (!fs.existsSync(absolute)) throw new CliError("not_found", `Config file not found: ${absolute}`);
4804
- const jiti = createJiti(import.meta.url, { interopDefault: true });
4805
- let loaded;
5011
+ let config;
4806
5012
  try {
4807
- loaded = await jiti.import(absolute);
5013
+ if (path.extname(absolute) === ".json") {
5014
+ config = JSON.parse(fs.readFileSync(absolute, "utf8"));
5015
+ if (config?.version !== 1) throw new Error("JSON flag config must have version: 1");
5016
+ } else if (path.extname(absolute) === ".go") config = await loadGoConfig(absolute);
5017
+ else {
5018
+ const loaded = await createJiti(import.meta.url, { interopDefault: true }).import(absolute);
5019
+ config = loaded.default ?? loaded;
5020
+ }
4808
5021
  } catch (error) {
4809
5022
  const message = error instanceof Error ? error.message : String(error);
4810
5023
  throw new CliError("validation_error", `Failed to load config ${absolute}: ${message}`);
4811
5024
  }
4812
- const config = loaded.default ?? loaded;
4813
- if (!config.flags || typeof config.flags !== "object") throw new CliError("validation_error", "shipd.config.ts must export defineFlags(...) result");
5025
+ if (!config?.flags || typeof config.flags !== "object" || Array.isArray(config.flags)) throw new CliError("validation_error", "Flag config must contain a flags object (defineFlags result or version-1 JSON)");
5026
+ for (const [key, flag] of Object.entries(config.flags)) {
5027
+ if (!flag || typeof flag !== "object" || Array.isArray(flag)) throw new CliError("validation_error", `Invalid definition for flag ${JSON.stringify(key)}`);
5028
+ if ("deprecated" in flag && typeof flag.deprecated !== "boolean") throw new CliError("validation_error", `Flag ${JSON.stringify(key)} deprecated must be a boolean`);
5029
+ }
4814
5030
  const candidates = Object.entries(config.flags).map(([key, flag]) => ({
4815
5031
  key,
4816
5032
  type: flag.type,
4817
5033
  default: flag.default,
4818
5034
  description: flag.description,
4819
5035
  tags: flag.tags ?? [],
4820
- lifecycle: flag.lifecycle ?? "active",
5036
+ lifecycle: flag.lifecycle ?? (flag.deprecated === true ? "deprecated" : "active"),
4821
5037
  variants: flag.variants,
4822
5038
  disabled: flag.disabled
4823
5039
  }));
@@ -4904,6 +5120,14 @@ function serializeTypeLiteral(value) {
4904
5120
  if (value === null) return "null";
4905
5121
  return JSON.stringify(value);
4906
5122
  }
5123
+ /** Pull preserves JSON config workflows rather than introducing a TS dependency. */
5124
+ function generateJSONConfigSource(flags) {
5125
+ const entries = flags.map(({ key, ...definition }) => [key, definition]);
5126
+ return `${JSON.stringify({
5127
+ version: 1,
5128
+ flags: Object.fromEntries(entries)
5129
+ }, null, 2)}\n`;
5130
+ }
4907
5131
 
4908
5132
  //#endregion
4909
5133
  //#region src/lib/plan.ts
@@ -5166,24 +5390,100 @@ function registerProjectsCommands(program) {
5166
5390
  });
5167
5391
  }
5168
5392
 
5393
+ //#endregion
5394
+ //#region src/lib/go-typegen.ts
5395
+ const GO_KEYWORDS = new Set("break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var".split(" "));
5396
+ function goName(value) {
5397
+ let name = (value.normalize("NFC").match(/[\p{L}\p{Nd}]+/gu) ?? []).map((word) => word[0].toUpperCase() + word.slice(1)).join("");
5398
+ if (!/^\p{Lu}/u.test(name)) name = `Flag${name}`;
5399
+ return name;
5400
+ }
5401
+ function generateGoTypesSource(flags, packageName = "flags") {
5402
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(packageName) || packageName === "_" || GO_KEYWORDS.has(packageName)) throw new CliError("validation_error", `Invalid Go package name ${JSON.stringify(packageName)}`);
5403
+ const used = /* @__PURE__ */ new Set(["Defaults"]);
5404
+ const claim = (name, key) => {
5405
+ if (used.has(name)) throw new CliError("validation_error", `Go identifier ${name} collides for flag ${JSON.stringify(key)}; rename the flag or variant`);
5406
+ used.add(name);
5407
+ return name;
5408
+ };
5409
+ const declarations = flags.map((flag) => {
5410
+ const name = claim(goName(flag.key), flag.key);
5411
+ const keyName = claim(`${name}Key`, flag.key);
5412
+ const lines = [`const ${keyName} = ${goString(flag.key)}`];
5413
+ let returnType;
5414
+ let expression;
5415
+ switch (flag.type) {
5416
+ case "boolean":
5417
+ returnType = "bool";
5418
+ expression = `client.IsEnabled(${keyName}, context)`;
5419
+ break;
5420
+ case "number":
5421
+ returnType = "float64";
5422
+ expression = `client.GetNumber(${keyName}, context)`;
5423
+ break;
5424
+ case "json":
5425
+ returnType = "any";
5426
+ expression = `client.GetJSON(${keyName}, context)`;
5427
+ break;
5428
+ case "variant":
5429
+ returnType = claim(`${name}Variant`, flag.key);
5430
+ lines.push(`type ${returnType} string`);
5431
+ for (const variant of flag.variants ?? []) {
5432
+ const constant = claim(`${name}${goName(variant)}`, flag.key);
5433
+ lines.push(`const ${constant} ${returnType} = ${goString(variant)}`);
5434
+ }
5435
+ expression = `${returnType}(client.GetVariant(${keyName}, context))`;
5436
+ }
5437
+ lines.push(`func ${name}(client *shipd.Client, context shipd.EvaluationContext) ${returnType} {\n\treturn ${expression}\n}`);
5438
+ return lines.join("\n\n");
5439
+ }).join("\n\n");
5440
+ const defaults = Object.fromEntries(flags.map((flag) => [flag.key, flag.default]));
5441
+ return `// Code generated by shipd types gen --language go; DO NOT EDIT.
5442
+ package ${packageName}
5443
+
5444
+ import (
5445
+ \t"encoding/json"
5446
+ ${flags.length ? " shipd \"github.com/alasti-company/useshipd.com/sdks/go\"\n" : ""})
5447
+
5448
+ // Defaults returns a fresh map of the config's defaults for shipd.Options.
5449
+ func Defaults() map[string]any {
5450
+ \tvar defaults map[string]any
5451
+ \t_ = json.Unmarshal([]byte(${JSON.stringify(JSON.stringify(defaults))}), &defaults)
5452
+ \treturn defaults
5453
+ }
5454
+
5455
+ ${declarations}
5456
+ `.trimEnd() + "\n";
5457
+ }
5458
+
5169
5459
  //#endregion
5170
5460
  //#region src/lib/typegen.ts
5171
- async function runTypegen(runtime, outPath = runtime.typesOut) {
5172
- const flags = await loadLocalFlags(runtime.configFile);
5461
+ async function runTypegen(runtime, outPath = runtime.typesOut, options = {}) {
5462
+ return writeTypegen(prepareTypegen(runtime, await loadLocalFlags(runtime.configFile), outPath, options));
5463
+ }
5464
+ function prepareTypegen(runtime, flags, outPath = runtime.typesOut, options = {}) {
5173
5465
  const absolute = path.resolve(outPath);
5174
- const source = generateTypesSource(flags);
5175
- fs.mkdirSync(path.dirname(absolute), { recursive: true });
5176
- fs.writeFileSync(absolute, source);
5466
+ const language = options.language ?? runtime.typesLanguage ?? "typescript";
5467
+ if (language !== "go" && language !== "typescript") throw new CliError("validation_error", "Type generation language must be typescript or go");
5177
5468
  return {
5469
+ source: language === "go" ? generateGoTypesSource(flags, options.packageName ?? runtime.typesPackage ?? "flags") : generateTypesSource(flags),
5178
5470
  written: absolute,
5179
5471
  flagCount: flags.length
5180
5472
  };
5181
5473
  }
5474
+ function writeTypegen(prepared) {
5475
+ fs.mkdirSync(path.dirname(prepared.written), { recursive: true });
5476
+ fs.writeFileSync(prepared.written, prepared.source);
5477
+ return {
5478
+ written: prepared.written,
5479
+ flagCount: prepared.flagCount
5480
+ };
5481
+ }
5182
5482
 
5183
5483
  //#endregion
5184
5484
  //#region src/commands/pull.ts
5185
5485
  function registerPullCommand(program) {
5186
- program.command("pull").description("Regenerate shipd.config.ts from the server and run typegen").action(async (_opts, command) => {
5486
+ program.command("pull").description("Regenerate the flag config from the server and run typegen").action(async (_opts, command) => {
5187
5487
  const runtime = getRuntime(command);
5188
5488
  const client = await createCliApiClient(runtime);
5189
5489
  const { projectId, environmentId } = await resolveIds(client, runtime);
@@ -5200,11 +5500,13 @@ function registerPullCommand(program) {
5200
5500
  variants: flag.variants,
5201
5501
  disabled: flag.disabled
5202
5502
  }));
5203
- const source = generateShipdConfigSource(localFlags);
5503
+ const packageName = path.extname(runtime.configFile) === ".go" && fs.existsSync(runtime.configFile) ? goPackageClause(fs.readFileSync(runtime.configFile, "utf8")).name : "main";
5504
+ const source = path.extname(runtime.configFile) === ".json" ? generateJSONConfigSource(localFlags) : path.extname(runtime.configFile) === ".go" ? generateGoConfigSource(localFlags, packageName) : generateShipdConfigSource(localFlags);
5505
+ const preparedTypes = prepareTypegen(runtime, localFlags);
5204
5506
  const outPath = path.resolve(runtime.configFile);
5205
5507
  fs.mkdirSync(path.dirname(outPath), { recursive: true });
5206
5508
  fs.writeFileSync(outPath, source);
5207
- const types = await runTypegen(runtime);
5509
+ const types = writeTypegen(preparedTypes);
5208
5510
  printResult(runtime, {
5209
5511
  written: outPath,
5210
5512
  flagCount: localFlags.length,
@@ -5259,6 +5561,7 @@ function registerPushCommand(program) {
5259
5561
  }
5260
5562
  if (planHasDefinitionChanges(plan) && !runtime.json) console.error("Note: definition/ownership changes apply to all environments (requires production permission).");
5261
5563
  if (!await confirm(runtime, "Apply this plan?")) throw new CliError("validation_error", "Push cancelled (pass --yes or --ci to apply non-interactively)");
5564
+ const preparedTypes = prepareTypegen(runtime, localFlags);
5262
5565
  const expectedVersions = expectedVersionsFromChanges(planned.changes);
5263
5566
  const applied = await client.flag.push({
5264
5567
  projectId,
@@ -5271,18 +5574,21 @@ function registerPushCommand(program) {
5271
5574
  expectedVersions
5272
5575
  }).catch(mapApiError);
5273
5576
  const appliedPlan = buildSyncPlan(applied.changes);
5274
- const types = await runTypegen(runtime);
5577
+ const types = writeTypegen(preparedTypes);
5275
5578
  const approvalRequests = applied.approvalRequests ?? [];
5579
+ const drafts = applied.drafts ?? [];
5276
5580
  printResult(runtime, {
5277
5581
  plan: appliedPlan,
5278
5582
  remoteOnly,
5279
5583
  forced: applied.forced === true,
5280
5584
  approvalRequests,
5281
- outcome: approvalRequests.length > 0 ? "pending_approval" : "applied",
5585
+ drafts,
5586
+ outcome: approvalRequests.length > 0 ? "pending_approval" : drafts.length > 0 ? "draft" : "applied",
5282
5587
  types
5283
5588
  }, [
5284
- applied.changes.length > 0 ? `Applied ${applied.changes.length} change(s).` : null,
5589
+ applied.changes.length > 0 ? `Saved ${applied.changes.length} change(s).` : null,
5285
5590
  approvalRequests.length > 0 ? `${approvalRequests.length} environment change(s) queued for approval.` : null,
5591
+ drafts.length > 0 ? `${drafts.length} targeting draft(s) ready to publish from the dashboard environment plan.` : null,
5286
5592
  applied.forced ? "Forced through version conflicts (audited)." : null,
5287
5593
  `Wrote types for ${types.flagCount} flag(s) to ${types.written}`
5288
5594
  ].filter(Boolean).join("\n"));
@@ -5366,9 +5672,14 @@ function registerSnapshotsCommands(program) {
5366
5672
  //#endregion
5367
5673
  //#region src/commands/types.ts
5368
5674
  function registerTypesCommands(program) {
5369
- program.command("types").description("Type generation helpers").command("gen").description("Generate TypeScript types from shipd.config.ts").option("-o, --out <path>", "Output file path").action(async (opts, command) => {
5675
+ program.command("types").description("Type generation helpers").command("gen").description("Generate TypeScript types or Go accessors from the local flag config").addOption(new Option("--language <language>", "Output language").choices(["typescript", "go"])).option("--package <name>", "Go package name (default: flags)").option("-o, --out <path>", "Output file path").action(async (opts, command) => {
5370
5676
  const runtime = getRuntime(command);
5371
- const result = await runTypegen(runtime, opts.out ?? runtime.typesOut);
5677
+ const language = opts.language ?? runtime.typesLanguage ?? "typescript";
5678
+ const defaultOutput = language === (runtime.typesLanguage ?? "typescript") ? runtime.typesOut : language === "go" ? "internal/flags/flags_gen.go" : "src/generated/flags.ts";
5679
+ const result = await runTypegen(runtime, opts.out ?? defaultOutput, {
5680
+ language,
5681
+ packageName: opts.package
5682
+ });
5372
5683
  printResult(runtime, result, `Wrote types for ${result.flagCount} flag(s) to ${result.written}`);
5373
5684
  });
5374
5685
  }
@@ -5377,7 +5688,7 @@ function registerTypesCommands(program) {
5377
5688
  //#region src/index.ts
5378
5689
  /** Detected before parse so usage errors can suppress Commander text under `--json`. */
5379
5690
  const jsonRequested = process.argv.includes("--json");
5380
- const program = new Command().name("shipd").description("shipd CLI — flags as code").option("--json", "Machine-readable JSON output", false).option("--yes", "Skip confirmation prompts", false).option("--ci", "Non-interactive mode (same as --yes / CI=1)", false).option("--api-url <url>", `API origin (default: ${DEFAULT_API_URL})`).option("--org <slug>", "Organization slug (session: select; API key: must match)").option("--project <slug>", "Project slug").option("--env <slug>", "Environment slug").option("--config <path>", "Project settings JSON (.shipd/config.json shape)").option("--flags-config <path>", "Path to shipd.config.ts").exitOverride().showHelpAfterError(!jsonRequested);
5691
+ const program = new Command().name("shipd").description("shipd CLI — flags as code").option("--json", "Machine-readable JSON output", false).option("--yes", "Skip confirmation prompts", false).option("--ci", "Non-interactive mode (same as --yes / CI=1)", false).option("--api-url <url>", `API origin (default: ${DEFAULT_API_URL})`).option("--org <slug>", "Organization slug (session: select; API key: must match)").option("--project <slug>", "Project slug").option("--env <slug>", "Environment slug").option("--config <path>", "Project settings JSON (.shipd/config.json shape)").option("--flags-config <path>", "Path to shipd.config.ts, shipd.config.go, or shipd.config.json").exitOverride().showHelpAfterError(!jsonRequested);
5381
5692
  if (jsonRequested) program.configureOutput({
5382
5693
  writeErr: () => {},
5383
5694
  writeOut: () => {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipdit/cli",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "shipd's command-line interface",
5
5
  "homepage": "https://docs.useshipd.com",
6
6
  "bugs": {
@@ -18,8 +18,8 @@
18
18
  "dependencies": {
19
19
  "commander": "^15.0.0",
20
20
  "jiti": "^2.5.1",
21
- "shipdit": "0.3.1",
22
- "zod": "^4.6.5"
21
+ "shipdit": "0.4.0",
22
+ "zod": "^4.6.7"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@shipd/api-client": "0.0.1",