@hexclave/shared 1.0.23 → 1.0.25

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 (93) hide show
  1. package/dist/ai/unified-prompts/reminders.js +1 -0
  2. package/dist/ai/unified-prompts/reminders.js.map +1 -1
  3. package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts.map +1 -1
  4. package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +15 -0
  5. package/dist/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
  6. package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +1 -46
  7. package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +1 -62
  8. package/dist/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -1
  9. package/dist/config/schema.d.ts +30 -30
  10. package/dist/config-authoring.d.ts +2 -2
  11. package/dist/config-authoring.d.ts.map +1 -1
  12. package/dist/config-authoring.js.map +1 -1
  13. package/dist/esm/ai/unified-prompts/reminders.js +1 -0
  14. package/dist/esm/ai/unified-prompts/reminders.js.map +1 -1
  15. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.d.ts.map +1 -1
  16. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js +15 -0
  17. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.js.map +1 -1
  18. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.d.ts +1 -46
  19. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js +1 -62
  20. package/dist/esm/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.js.map +1 -1
  21. package/dist/esm/config/schema.d.ts +30 -30
  22. package/dist/esm/config-authoring.d.ts +2 -2
  23. package/dist/esm/config-authoring.d.ts.map +1 -1
  24. package/dist/esm/config-authoring.js.map +1 -1
  25. package/dist/esm/interface/admin-metrics.d.ts +10 -10
  26. package/dist/esm/interface/conversations.d.ts +11 -11
  27. package/dist/esm/interface/crud/current-user.d.ts +8 -8
  28. package/dist/esm/interface/crud/email-outbox.d.ts +184 -184
  29. package/dist/esm/interface/crud/invoices.d.ts +2 -2
  30. package/dist/esm/interface/crud/products.d.ts +15 -15
  31. package/dist/esm/interface/crud/products.d.ts.map +1 -1
  32. package/dist/esm/interface/crud/project-api-keys.d.ts +4 -4
  33. package/dist/esm/interface/crud/projects.d.ts +50 -0
  34. package/dist/esm/interface/crud/projects.d.ts.map +1 -1
  35. package/dist/esm/interface/crud/projects.js +6 -0
  36. package/dist/esm/interface/crud/projects.js.map +1 -1
  37. package/dist/esm/interface/crud/team-member-profiles.d.ts +12 -12
  38. package/dist/esm/interface/crud/transactions.d.ts +20 -20
  39. package/dist/esm/interface/crud/transactions.d.ts.map +1 -1
  40. package/dist/esm/interface/crud/users.d.ts +8 -8
  41. package/dist/esm/interface/webhooks.d.ts +2 -2
  42. package/dist/esm/known-errors.d.ts +1 -1
  43. package/dist/esm/schema-fields.d.ts +1 -1
  44. package/dist/esm/sessions.d.ts +7 -7
  45. package/dist/esm/utils/env.d.ts +2 -1
  46. package/dist/esm/utils/env.d.ts.map +1 -1
  47. package/dist/esm/utils/env.js +29 -17
  48. package/dist/esm/utils/env.js.map +1 -1
  49. package/dist/esm/utils/env.test.d.ts +1 -0
  50. package/dist/esm/utils/env.test.js +49 -0
  51. package/dist/esm/utils/env.test.js.map +1 -0
  52. package/dist/esm/utils/errors.d.ts.map +1 -1
  53. package/dist/esm/utils/errors.js +4 -1
  54. package/dist/esm/utils/errors.js.map +1 -1
  55. package/dist/interface/admin-metrics.d.ts +10 -10
  56. package/dist/interface/conversations.d.ts +11 -11
  57. package/dist/interface/crud/current-user.d.ts +8 -8
  58. package/dist/interface/crud/email-outbox.d.ts +184 -184
  59. package/dist/interface/crud/invoices.d.ts +2 -2
  60. package/dist/interface/crud/products.d.ts +15 -15
  61. package/dist/interface/crud/products.d.ts.map +1 -1
  62. package/dist/interface/crud/project-api-keys.d.ts +4 -4
  63. package/dist/interface/crud/projects.d.ts +50 -0
  64. package/dist/interface/crud/projects.d.ts.map +1 -1
  65. package/dist/interface/crud/projects.js +6 -0
  66. package/dist/interface/crud/projects.js.map +1 -1
  67. package/dist/interface/crud/team-member-profiles.d.ts +12 -12
  68. package/dist/interface/crud/transactions.d.ts +20 -20
  69. package/dist/interface/crud/transactions.d.ts.map +1 -1
  70. package/dist/interface/crud/users.d.ts +8 -8
  71. package/dist/interface/webhooks.d.ts +2 -2
  72. package/dist/known-errors.d.ts +1 -1
  73. package/dist/schema-fields.d.ts +1 -1
  74. package/dist/sessions.d.ts +7 -7
  75. package/dist/utils/env.d.ts +2 -1
  76. package/dist/utils/env.d.ts.map +1 -1
  77. package/dist/utils/env.js +29 -16
  78. package/dist/utils/env.js.map +1 -1
  79. package/dist/utils/env.test.d.ts +1 -0
  80. package/dist/utils/env.test.js +49 -0
  81. package/dist/utils/env.test.js.map +1 -0
  82. package/dist/utils/errors.d.ts.map +1 -1
  83. package/dist/utils/errors.js +4 -1
  84. package/dist/utils/errors.js.map +1 -1
  85. package/package.json +1 -1
  86. package/src/ai/unified-prompts/reminders.ts +1 -0
  87. package/src/ai/unified-prompts/skill-site-prompt-parts/ai-setup-prompt.ts +15 -0
  88. package/src/ai/unified-prompts/skill-site-prompt-parts/docs-json.generated.ts +1 -62
  89. package/src/config-authoring.ts +2 -2
  90. package/src/interface/crud/projects.ts +12 -0
  91. package/src/utils/env.test.tsx +58 -0
  92. package/src/utils/env.tsx +36 -20
  93. package/src/utils/errors.tsx +6 -1
@@ -27,11 +27,11 @@ type StrictStackConfig<T extends StackConfig> =
27
27
  : T;
28
28
 
29
29
  /** @deprecated Use `defineHexclaveConfig` from the `@hexclave/*` package instead — same symbol, new brand name. See https://docs.hexclave.com/migration. */
30
- export function defineStackConfig<const T extends StackConfig>(config: StrictStackConfig<T>): T {
30
+ export function defineStackConfig(config: StrictStackConfig<StackConfig>): StackConfig {
31
31
  return config;
32
32
  }
33
33
 
34
34
  // Hexclave alias — separate function so it does not inherit the deprecation tag.
35
- export function defineHexclaveConfig<const T extends HexclaveConfig>(config: StrictStackConfig<T>): T {
35
+ export function defineHexclaveConfig(config: StrictStackConfig<HexclaveConfig>): HexclaveConfig {
36
36
  return config;
37
37
  }
@@ -32,6 +32,14 @@ const enabledOAuthProviderSchema = yupObject({
32
32
  id: schemaFields.oauthIdSchema.defined(),
33
33
  });
34
34
 
35
+ const pushedConfigErrorSchema = yupObject({
36
+ message: yupString().defined(),
37
+ }).defined();
38
+
39
+ const configWarningSchema = yupObject({
40
+ message: yupString().defined(),
41
+ }).defined();
42
+
35
43
  const onboardingConfigChoiceValues = ["create-new", "link-existing"] as const;
36
44
  const onboardingSignInMethodValues = ["credential", "magicLink", "passkey", "google", "github", "microsoft"] as const;
37
45
  const onboardingPaymentsCountryValues = ["US", "OTHER"] as const;
@@ -89,6 +97,8 @@ export const projectsCrudAdminReadSchema = yupObject({
89
97
  owner_team_id: schemaFields.yupString().nullable().defined(),
90
98
  onboarding_status: schemaFields.projectOnboardingStatusSchema.defined(),
91
99
  onboarding_state: projectOnboardingStateSchema.nullable().optional(),
100
+ pushed_config_error: pushedConfigErrorSchema.nullable().defined(),
101
+ config_warnings: yupArray(configWarningSchema).defined(),
92
102
  /** @deprecated */
93
103
  config: yupObject({
94
104
  allow_localhost: schemaFields.projectAllowLocalhostSchema.defined(),
@@ -117,6 +127,8 @@ export const projectsCrudAdminReadSchema = yupObject({
117
127
  export const projectsCrudClientReadSchema = yupObject({
118
128
  id: schemaFields.projectIdSchema.defined(),
119
129
  display_name: schemaFields.projectDisplayNameSchema.defined(),
130
+ pushed_config_error: pushedConfigErrorSchema.nullable().defined(),
131
+ config_warnings: yupArray(configWarningSchema).defined(),
120
132
  config: yupObject({
121
133
  sign_up_enabled: schemaFields.projectSignUpEnabledSchema.defined(),
122
134
  credential_enabled: schemaFields.projectCredentialEnabledSchema.defined(),
@@ -0,0 +1,58 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { getEnvVariable, getProcessEnv, resolveHexclaveStackEnvVarValue } from "./env";
3
+
4
+ afterEach(() => {
5
+ vi.unstubAllEnvs();
6
+ });
7
+
8
+ describe("Hexclave/Stack env var dual-read", () => {
9
+ it("falls back to the legacy Stack name when the Hexclave value is empty", () => {
10
+ vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_API_URL", "");
11
+ vi.stubEnv("NEXT_PUBLIC_STACK_API_URL", "https://stack.example.test");
12
+
13
+ expect(getEnvVariable("NEXT_PUBLIC_STACK_API_URL")).toBe("https://stack.example.test");
14
+ expect(getProcessEnv("NEXT_PUBLIC_STACK_API_URL")).toBe("https://stack.example.test");
15
+ });
16
+
17
+ it("allows both names when they have the same non-empty value", () => {
18
+ vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_API_URL", "https://api.example.test");
19
+ vi.stubEnv("NEXT_PUBLIC_STACK_API_URL", "https://api.example.test");
20
+
21
+ expect(getEnvVariable("NEXT_PUBLIC_STACK_API_URL")).toBe("https://api.example.test");
22
+ expect(getProcessEnv("NEXT_PUBLIC_STACK_API_URL")).toBe("https://api.example.test");
23
+ });
24
+
25
+ it("throws when both names are non-empty and different", () => {
26
+ vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_API_URL", "https://hexclave.example.test");
27
+ vi.stubEnv("NEXT_PUBLIC_STACK_API_URL", "https://stack.example.test");
28
+
29
+ expect(() => getEnvVariable("NEXT_PUBLIC_STACK_API_URL")).toThrow(/NEXT_PUBLIC_HEXCLAVE_API_URL.*NEXT_PUBLIC_STACK_API_URL.*different values/);
30
+ expect(() => getProcessEnv("NEXT_PUBLIC_STACK_API_URL")).toThrow(/NEXT_PUBLIC_HEXCLAVE_API_URL.*NEXT_PUBLIC_STACK_API_URL.*different values/);
31
+ });
32
+
33
+ it("checks renamed legacy aliases when falling back", () => {
34
+ vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_URL", "https://hexclave-url.example.test");
35
+ vi.stubEnv("NEXT_PUBLIC_STACK_URL", "https://stack-url.example.test");
36
+
37
+ expect(() => getEnvVariable("NEXT_PUBLIC_STACK_API_URL")).toThrow(/NEXT_PUBLIC_HEXCLAVE_URL.*NEXT_PUBLIC_STACK_URL.*different values/);
38
+ });
39
+
40
+ it("returns undefined when both names are empty", () => {
41
+ expect(resolveHexclaveStackEnvVarValue("HEXCLAVE_FOO", "STACK_FOO", "", "")).toBeUndefined();
42
+ });
43
+
44
+ it("falls back to the legacy Stack name when a canonical Hexclave name is looked up", () => {
45
+ vi.stubEnv("NEXT_PUBLIC_STACK_API_URL", "https://stack.example.test");
46
+
47
+ // Caller passes the canonical HEXCLAVE_ name but only the legacy value is set.
48
+ expect(getEnvVariable("NEXT_PUBLIC_HEXCLAVE_API_URL")).toBe("https://stack.example.test");
49
+ expect(getProcessEnv("NEXT_PUBLIC_HEXCLAVE_API_URL")).toBe("https://stack.example.test");
50
+ });
51
+
52
+ it("throws on a conflict when a canonical Hexclave name is looked up", () => {
53
+ vi.stubEnv("NEXT_PUBLIC_HEXCLAVE_API_URL", "https://hexclave.example.test");
54
+ vi.stubEnv("NEXT_PUBLIC_STACK_API_URL", "https://stack.example.test");
55
+
56
+ expect(() => getEnvVariable("NEXT_PUBLIC_HEXCLAVE_API_URL")).toThrow(/NEXT_PUBLIC_HEXCLAVE_API_URL.*NEXT_PUBLIC_STACK_API_URL.*different values/);
57
+ });
58
+ });
package/src/utils/env.tsx CHANGED
@@ -6,22 +6,37 @@ export function isBrowserLike() {
6
6
  }
7
7
 
8
8
  // newName: oldName
9
- const ENV_VAR_RENAME: Record<string, string[]> = {
9
+ const ENV_VAR_RENAME: Record<string, string[] | undefined> = {
10
10
  NEXT_PUBLIC_STACK_API_URL: ['STACK_BASE_URL', 'NEXT_PUBLIC_STACK_URL'],
11
11
  };
12
12
 
13
+ export function resolveHexclaveStackEnvVarValue(hexclaveName: string, stackName: string, hexclaveValue: string | undefined, stackValue: string | undefined): string | undefined {
14
+ if (hexclaveValue && stackValue && hexclaveValue !== stackValue) {
15
+ throw new Error(`Environment variables ${hexclaveName} and ${stackName} are both set to different values. Remove one of them or set them to the same value.`);
16
+ }
17
+ return hexclaveValue || stackValue || undefined;
18
+ }
19
+
13
20
  /**
14
- * Hexclave rebrand: compute the `HEXCLAVE_*`-prefixed equivalent of a `STACK_*`
15
- * env var name by replacing the first `STACK_` occurrence with `HEXCLAVE_`.
16
- * Covers `STACK_FOO`, `NEXT_PUBLIC_STACK_FOO`, `NEXT_PUBLIC_BROWSER_STACK_FOO`,
17
- * `NEXT_PUBLIC_SERVER_STACK_FOO`, `VITE_STACK_FOO`. Returns `undefined` when the
18
- * name has no `STACK_` segment (caller should behave exactly as before).
21
+ * Hexclave rebrand: resolve an env var by reading both the `HEXCLAVE_*` and
22
+ * `STACK_*` spellings, preferring the canonical Hexclave value and falling back
23
+ * to the legacy Stack value (empty counts as unset). Works in BOTH directions —
24
+ * whether the caller passes the legacy `STACK_FOO` name or the canonical
25
+ * `HEXCLAVE_FOO` name, the other spelling is still honored. Covers `STACK_FOO`,
26
+ * `NEXT_PUBLIC_STACK_FOO`, `NEXT_PUBLIC_BROWSER_STACK_FOO`,
27
+ * `NEXT_PUBLIC_SERVER_STACK_FOO`, `VITE_STACK_FOO` and their HEXCLAVE_ twins.
28
+ * Names with neither segment behave exactly as before.
19
29
  */
20
- function getHexclaveEnvVarName(name: string): string | undefined {
21
- if (!name.includes("STACK_")) {
22
- return undefined;
30
+ function getEnvVarWithHexclaveFallback(name: string): string | undefined {
31
+ if (name.includes("STACK_")) {
32
+ const hexclaveName = name.replace("STACK_", "HEXCLAVE_");
33
+ return resolveHexclaveStackEnvVarValue(hexclaveName, name, process.env[hexclaveName], process.env[name]);
34
+ }
35
+ if (name.includes("HEXCLAVE_")) {
36
+ const stackName = name.replace("HEXCLAVE_", "STACK_");
37
+ return resolveHexclaveStackEnvVarValue(name, stackName, process.env[name], process.env[stackName]);
23
38
  }
24
- return name.replace("STACK_", "HEXCLAVE_");
39
+ return process.env[name];
25
40
  }
26
41
 
27
42
  /**
@@ -45,21 +60,21 @@ export function getEnvVariable(name: string, defaultValue?: string | undefined):
45
60
 
46
61
  // throw error if the old name is used as the retrieve key
47
62
  for (const [newName, oldNames] of Object.entries(ENV_VAR_RENAME)) {
48
- if (oldNames.includes(name)) {
63
+ if (oldNames?.includes(name)) {
49
64
  throwErr(`Environment variable ${name} has been renamed to ${newName}. Please update your configuration to use the new name.`);
50
65
  }
51
66
  }
52
67
 
53
68
  // Hexclave rebrand: prefer the HEXCLAVE_*-prefixed equivalent, fall back to the STACK_* name.
54
- const hexclaveName = getHexclaveEnvVarName(name);
55
- let value = (hexclaveName ? process.env[hexclaveName] : undefined) ?? process.env[name];
69
+ // Treat the empty string as unset — the checked-in .env templates define empty
70
+ // HEXCLAVE_* placeholders, which must not shadow a real value under the legacy name.
71
+ let value = getEnvVarWithHexclaveFallback(name);
56
72
 
57
73
  // check the key under the old name if the new name is not found
58
- if (!value && ENV_VAR_RENAME[name] as any) {
59
- for (const oldName of ENV_VAR_RENAME[name]) {
60
- // Hexclave rebrand: also accept the HEXCLAVE_*-prefixed equivalent of each old alias.
61
- const hexclaveOldName = getHexclaveEnvVarName(oldName);
62
- value = (hexclaveOldName ? process.env[hexclaveOldName] : undefined) ?? process.env[oldName];
74
+ const renamedNames = ENV_VAR_RENAME[name];
75
+ if (!value && renamedNames != null) {
76
+ for (const oldName of renamedNames) {
77
+ value = getEnvVarWithHexclaveFallback(oldName);
63
78
  if (value) break;
64
79
  }
65
80
  }
@@ -112,6 +127,7 @@ export function getProcessEnv(name: string): string | undefined {
112
127
  return undefined;
113
128
  }
114
129
  // Hexclave rebrand: prefer the HEXCLAVE_*-prefixed equivalent, fall back to the STACK_* name.
115
- const hexclaveName = getHexclaveEnvVarName(name);
116
- return (hexclaveName ? process.env[hexclaveName] : undefined) ?? process.env[name];
130
+ // Empty counts as unset — the checked-in .env templates define empty HEXCLAVE_* placeholders,
131
+ // which must not shadow a real value under the legacy name.
132
+ return getEnvVarWithHexclaveFallback(name);
117
133
  }
@@ -80,7 +80,12 @@ export class HexclaveAssertionError extends Error {
80
80
 
81
81
  // Use literal dot-form (guarded with `typeof process`) so Next.js / webpack
82
82
  // DefinePlugin can inline the value at build time. See getProcessEnv in ./env.
83
- if ((typeof process !== "undefined" ? process.env.NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR : undefined) === "true") {
83
+ const hexclaveDebuggerValue = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_HEXCLAVE_DEBUGGER_ON_ASSERTION_ERROR : undefined;
84
+ const stackDebuggerValue = typeof process !== "undefined" ? process.env.NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR : undefined;
85
+ if (hexclaveDebuggerValue && stackDebuggerValue && hexclaveDebuggerValue !== stackDebuggerValue) {
86
+ throw new Error("Environment variables NEXT_PUBLIC_HEXCLAVE_DEBUGGER_ON_ASSERTION_ERROR and NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR are both set to different values. Remove one of them or set them to the same value.");
87
+ }
88
+ if ((hexclaveDebuggerValue || stackDebuggerValue) === "true") {
84
89
  debugger;
85
90
  }
86
91
  }