@superblocksteam/shared 0.9586.6 → 0.9589.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 (200) hide show
  1. package/dist/database-lifecycle/index.d.ts +115 -0
  2. package/dist/database-lifecycle/index.d.ts.map +1 -0
  3. package/dist/database-lifecycle/index.js +143 -0
  4. package/dist/database-lifecycle/index.js.map +1 -0
  5. package/dist/database-lifecycle/index.test.d.ts +2 -0
  6. package/dist/database-lifecycle/index.test.d.ts.map +1 -0
  7. package/dist/database-lifecycle/index.test.js +110 -0
  8. package/dist/database-lifecycle/index.test.js.map +1 -0
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/plugins/index.d.ts.map +1 -1
  14. package/dist/plugins/index.js +4 -0
  15. package/dist/plugins/index.js.map +1 -1
  16. package/dist/plugins/templates/dropbox.d.ts.map +1 -1
  17. package/dist/plugins/templates/dropbox.js +5 -1
  18. package/dist/plugins/templates/dropbox.js.map +1 -1
  19. package/dist/plugins/templates/shared/auth.d.ts +2 -0
  20. package/dist/plugins/templates/shared/auth.d.ts.map +1 -1
  21. package/dist/plugins/templates/shared/auth.js +1 -0
  22. package/dist/plugins/templates/shared/auth.js.map +1 -1
  23. package/dist/types/ai/index.d.ts +1 -0
  24. package/dist/types/ai/index.d.ts.map +1 -1
  25. package/dist/types/ai/index.js +1 -0
  26. package/dist/types/ai/index.js.map +1 -1
  27. package/dist/types/ai/provider-credential.d.ts +53 -0
  28. package/dist/types/ai/provider-credential.d.ts.map +1 -0
  29. package/dist/types/ai/provider-credential.js +10 -0
  30. package/dist/types/ai/provider-credential.js.map +1 -0
  31. package/dist/types/audit/ocsf.d.ts.map +1 -1
  32. package/dist/types/audit/ocsf.js +8 -2
  33. package/dist/types/audit/ocsf.js.map +1 -1
  34. package/dist/types/commit/index.d.ts +1 -0
  35. package/dist/types/commit/index.d.ts.map +1 -1
  36. package/dist/types/commit/index.js.map +1 -1
  37. package/dist/types/common/env.d.ts +25 -0
  38. package/dist/types/common/env.d.ts.map +1 -1
  39. package/dist/types/common/env.js +29 -1
  40. package/dist/types/common/env.js.map +1 -1
  41. package/dist/types/common/env.test.d.ts +2 -0
  42. package/dist/types/common/env.test.d.ts.map +1 -0
  43. package/dist/types/common/env.test.js +24 -0
  44. package/dist/types/common/env.test.js.map +1 -0
  45. package/dist/types/common/response.d.ts +2 -2
  46. package/dist/types/common/response.d.ts.map +1 -1
  47. package/dist/types/common/response.js.map +1 -1
  48. package/dist/types/credentials/index.d.ts +7 -7
  49. package/dist/types/credentials/index.d.ts.map +1 -1
  50. package/dist/types/index.d.ts +2 -0
  51. package/dist/types/index.d.ts.map +1 -1
  52. package/dist/types/index.js +2 -0
  53. package/dist/types/index.js.map +1 -1
  54. package/dist/types/integration/index.d.ts +5 -2
  55. package/dist/types/integration/index.d.ts.map +1 -1
  56. package/dist/types/integration/index.js +5 -2
  57. package/dist/types/integration/index.js.map +1 -1
  58. package/dist/types/organization/agent.js +10 -40
  59. package/dist/types/organization/agent.js.map +1 -1
  60. package/dist/types/plugin/form.d.ts +1 -0
  61. package/dist/types/plugin/form.d.ts.map +1 -1
  62. package/dist/types/policyGate/index.d.ts +32 -0
  63. package/dist/types/policyGate/index.d.ts.map +1 -0
  64. package/dist/types/policyGate/index.js +3 -0
  65. package/dist/types/policyGate/index.js.map +1 -0
  66. package/dist/types/rbac/index.d.ts +8 -0
  67. package/dist/types/rbac/index.d.ts.map +1 -1
  68. package/dist/types/rbac/index.js +5 -0
  69. package/dist/types/rbac/index.js.map +1 -1
  70. package/dist/types/response/GetPolicyGateReadinessResponseBody.d.ts +3 -0
  71. package/dist/types/response/GetPolicyGateReadinessResponseBody.d.ts.map +1 -0
  72. package/dist/types/response/GetPolicyGateReadinessResponseBody.js +3 -0
  73. package/dist/types/response/GetPolicyGateReadinessResponseBody.js.map +1 -0
  74. package/dist/types/response/index.d.ts +1 -0
  75. package/dist/types/response/index.d.ts.map +1 -1
  76. package/dist/types/response/index.js +1 -0
  77. package/dist/types/response/index.js.map +1 -1
  78. package/dist/types/reviewPolicy/index.d.ts +105 -0
  79. package/dist/types/reviewPolicy/index.d.ts.map +1 -0
  80. package/dist/types/reviewPolicy/index.js +47 -0
  81. package/dist/types/reviewPolicy/index.js.map +1 -0
  82. package/dist/utils/git-url.d.ts +0 -1
  83. package/dist/utils/git-url.d.ts.map +1 -1
  84. package/dist/utils/git-url.js +1 -2
  85. package/dist/utils/git-url.js.map +1 -1
  86. package/dist/utils/git-url.test.js +0 -5
  87. package/dist/utils/git-url.test.js.map +1 -1
  88. package/dist-esm/database-lifecycle/index.d.ts +115 -0
  89. package/dist-esm/database-lifecycle/index.d.ts.map +1 -0
  90. package/dist-esm/database-lifecycle/index.js +131 -0
  91. package/dist-esm/database-lifecycle/index.js.map +1 -0
  92. package/dist-esm/database-lifecycle/index.test.d.ts +2 -0
  93. package/dist-esm/database-lifecycle/index.test.d.ts.map +1 -0
  94. package/dist-esm/database-lifecycle/index.test.js +108 -0
  95. package/dist-esm/database-lifecycle/index.test.js.map +1 -0
  96. package/dist-esm/index.d.ts +1 -0
  97. package/dist-esm/index.d.ts.map +1 -1
  98. package/dist-esm/index.js +1 -0
  99. package/dist-esm/index.js.map +1 -1
  100. package/dist-esm/plugins/index.d.ts.map +1 -1
  101. package/dist-esm/plugins/index.js +4 -0
  102. package/dist-esm/plugins/index.js.map +1 -1
  103. package/dist-esm/plugins/templates/dropbox.d.ts.map +1 -1
  104. package/dist-esm/plugins/templates/dropbox.js +5 -1
  105. package/dist-esm/plugins/templates/dropbox.js.map +1 -1
  106. package/dist-esm/plugins/templates/shared/auth.d.ts +2 -0
  107. package/dist-esm/plugins/templates/shared/auth.d.ts.map +1 -1
  108. package/dist-esm/plugins/templates/shared/auth.js +1 -0
  109. package/dist-esm/plugins/templates/shared/auth.js.map +1 -1
  110. package/dist-esm/types/ai/index.d.ts +1 -0
  111. package/dist-esm/types/ai/index.d.ts.map +1 -1
  112. package/dist-esm/types/ai/index.js +1 -0
  113. package/dist-esm/types/ai/index.js.map +1 -1
  114. package/dist-esm/types/ai/provider-credential.d.ts +53 -0
  115. package/dist-esm/types/ai/provider-credential.d.ts.map +1 -0
  116. package/dist-esm/types/ai/provider-credential.js +7 -0
  117. package/dist-esm/types/ai/provider-credential.js.map +1 -0
  118. package/dist-esm/types/audit/ocsf.d.ts.map +1 -1
  119. package/dist-esm/types/audit/ocsf.js +8 -2
  120. package/dist-esm/types/audit/ocsf.js.map +1 -1
  121. package/dist-esm/types/commit/index.d.ts +1 -0
  122. package/dist-esm/types/commit/index.d.ts.map +1 -1
  123. package/dist-esm/types/commit/index.js.map +1 -1
  124. package/dist-esm/types/common/env.d.ts +25 -0
  125. package/dist-esm/types/common/env.d.ts.map +1 -1
  126. package/dist-esm/types/common/env.js +27 -0
  127. package/dist-esm/types/common/env.js.map +1 -1
  128. package/dist-esm/types/common/env.test.d.ts +2 -0
  129. package/dist-esm/types/common/env.test.d.ts.map +1 -0
  130. package/dist-esm/types/common/env.test.js +22 -0
  131. package/dist-esm/types/common/env.test.js.map +1 -0
  132. package/dist-esm/types/common/response.d.ts +2 -2
  133. package/dist-esm/types/common/response.d.ts.map +1 -1
  134. package/dist-esm/types/common/response.js.map +1 -1
  135. package/dist-esm/types/credentials/index.d.ts +7 -7
  136. package/dist-esm/types/credentials/index.d.ts.map +1 -1
  137. package/dist-esm/types/index.d.ts +2 -0
  138. package/dist-esm/types/index.d.ts.map +1 -1
  139. package/dist-esm/types/index.js +2 -0
  140. package/dist-esm/types/index.js.map +1 -1
  141. package/dist-esm/types/integration/index.d.ts +5 -2
  142. package/dist-esm/types/integration/index.d.ts.map +1 -1
  143. package/dist-esm/types/integration/index.js +5 -2
  144. package/dist-esm/types/integration/index.js.map +1 -1
  145. package/dist-esm/types/organization/agent.js +1 -1
  146. package/dist-esm/types/organization/agent.js.map +1 -1
  147. package/dist-esm/types/plugin/form.d.ts +1 -0
  148. package/dist-esm/types/plugin/form.d.ts.map +1 -1
  149. package/dist-esm/types/policyGate/index.d.ts +32 -0
  150. package/dist-esm/types/policyGate/index.d.ts.map +1 -0
  151. package/dist-esm/types/policyGate/index.js +2 -0
  152. package/dist-esm/types/policyGate/index.js.map +1 -0
  153. package/dist-esm/types/rbac/index.d.ts +8 -0
  154. package/dist-esm/types/rbac/index.d.ts.map +1 -1
  155. package/dist-esm/types/rbac/index.js +5 -0
  156. package/dist-esm/types/rbac/index.js.map +1 -1
  157. package/dist-esm/types/response/GetPolicyGateReadinessResponseBody.d.ts +3 -0
  158. package/dist-esm/types/response/GetPolicyGateReadinessResponseBody.d.ts.map +1 -0
  159. package/dist-esm/types/response/GetPolicyGateReadinessResponseBody.js +2 -0
  160. package/dist-esm/types/response/GetPolicyGateReadinessResponseBody.js.map +1 -0
  161. package/dist-esm/types/response/index.d.ts +1 -0
  162. package/dist-esm/types/response/index.d.ts.map +1 -1
  163. package/dist-esm/types/response/index.js +1 -0
  164. package/dist-esm/types/response/index.js.map +1 -1
  165. package/dist-esm/types/reviewPolicy/index.d.ts +105 -0
  166. package/dist-esm/types/reviewPolicy/index.d.ts.map +1 -0
  167. package/dist-esm/types/reviewPolicy/index.js +44 -0
  168. package/dist-esm/types/reviewPolicy/index.js.map +1 -0
  169. package/dist-esm/utils/git-url.d.ts +0 -1
  170. package/dist-esm/utils/git-url.d.ts.map +1 -1
  171. package/dist-esm/utils/git-url.js +0 -1
  172. package/dist-esm/utils/git-url.js.map +1 -1
  173. package/dist-esm/utils/git-url.test.js +1 -6
  174. package/dist-esm/utils/git-url.test.js.map +1 -1
  175. package/package.json +3 -3
  176. package/src/database-lifecycle/index.test.ts +132 -0
  177. package/src/database-lifecycle/index.ts +278 -0
  178. package/src/index.ts +1 -0
  179. package/src/plugins/index.ts +4 -0
  180. package/src/plugins/templates/dropbox.ts +5 -1
  181. package/src/plugins/templates/shared/auth.ts +3 -0
  182. package/src/types/ai/index.ts +1 -0
  183. package/src/types/ai/provider-credential.ts +61 -0
  184. package/src/types/audit/ocsf.ts +8 -2
  185. package/src/types/commit/index.ts +1 -0
  186. package/src/types/common/env.test.ts +27 -0
  187. package/src/types/common/env.ts +28 -0
  188. package/src/types/common/response.ts +2 -2
  189. package/src/types/credentials/index.ts +7 -7
  190. package/src/types/index.ts +2 -0
  191. package/src/types/integration/index.ts +9 -2
  192. package/src/types/organization/agent.ts +1 -1
  193. package/src/types/plugin/form.ts +1 -0
  194. package/src/types/policyGate/index.ts +52 -0
  195. package/src/types/rbac/index.ts +6 -0
  196. package/src/types/response/GetPolicyGateReadinessResponseBody.ts +3 -0
  197. package/src/types/response/index.ts +1 -0
  198. package/src/types/reviewPolicy/index.ts +132 -0
  199. package/src/utils/git-url.test.ts +1 -14
  200. package/src/utils/git-url.ts +0 -2
@@ -0,0 +1,278 @@
1
+ import { sha256Base64 } from '../signing/hashing.js';
2
+
3
+ export const LIFECYCLE_TERMINAL_STATES = ['ready', 'failed', 'cancelled'] as const;
4
+ export const LIFECYCLE_NON_TERMINAL_STATES = ['pending', 'provisioning', 'migrating', 'retiring'] as const;
5
+ export const LIFECYCLE_STATES = [...LIFECYCLE_NON_TERMINAL_STATES, ...LIFECYCLE_TERMINAL_STATES] as const;
6
+ export const LIFECYCLE_MIGRATION_STATES = ['pending', 'migrated', 'failed'] as const;
7
+ export const LIFECYCLE_OPERATIONS = ['ensure_dev_database', 'ensure_prod_database', 'migrate_schema', 'retire_database'] as const;
8
+ export const ENVIRONMENT_CLASSES = ['dev', 'staging', 'prod'] as const;
9
+ export const DATABASE_ENGINES = ['postgres', 'snowflake', 'snowflake_postgres', 'lakebase'] as const;
10
+ export const DATABASE_LIFECYCLE_MANAGED_BY = 'database_lifecycle';
11
+
12
+ export type EnvironmentClass = (typeof ENVIRONMENT_CLASSES)[number];
13
+ export type DatabaseEngine = (typeof DATABASE_ENGINES)[number];
14
+ export type LifecycleOperation = (typeof LIFECYCLE_OPERATIONS)[number];
15
+ export type LifecycleTerminalState = (typeof LIFECYCLE_TERMINAL_STATES)[number];
16
+ export type LifecycleNonTerminalState = (typeof LIFECYCLE_NON_TERMINAL_STATES)[number];
17
+ export type LifecycleState = (typeof LIFECYCLE_STATES)[number];
18
+ export type LifecycleMigrationState = (typeof LIFECYCLE_MIGRATION_STATES)[number];
19
+
20
+ export type LifecycleErrorCode =
21
+ | 'unsupported_provider_capability'
22
+ | 'backend_locked'
23
+ | 'policy_blocked'
24
+ | 'credential_resolution_failed'
25
+ | 'terraform_failed'
26
+ | 'callback_failed';
27
+
28
+ export const CREDENTIAL_RESOLVERS = ['opa_local', 'aws_secrets_manager', 'gcp_secret_manager', 'vault', 'kubernetes_secret'] as const;
29
+ export type CredentialResolver = (typeof CREDENTIAL_RESOLVERS)[number];
30
+
31
+ export type CredentialRef = {
32
+ resolver: CredentialResolver;
33
+ ref: string;
34
+ field?: string;
35
+ };
36
+
37
+ export type DatabaseRequirement = {
38
+ logicalName: string;
39
+ engine: DatabaseEngine;
40
+ version?: string;
41
+ sizing?: Record<string, unknown>;
42
+ extensions?: readonly string[];
43
+ replicaCount?: number;
44
+ migrationDirectory?: string;
45
+ };
46
+
47
+ export type TerraformDatabaseBackend = {
48
+ provisioner: 'terraform';
49
+ provider: 'aws-rds' | 'snowflake' | 'databricks';
50
+ stateBackend: 's3' | 'gcs' | 'azurerm' | 'local';
51
+ remoteState: boolean;
52
+ locking: boolean;
53
+ };
54
+
55
+ export type DatabaseBackend = TerraformDatabaseBackend;
56
+
57
+ export type EnvironmentProfile = {
58
+ id: string;
59
+ organizationId: string;
60
+ environmentClass: EnvironmentClass;
61
+ environmentName: string;
62
+ opaAgentId: string;
63
+ supportedOperations: LifecycleOperation[];
64
+ supportedEngines: DatabaseEngine[];
65
+ backend: DatabaseBackend;
66
+ };
67
+
68
+ type DatabaseBindingBase = {
69
+ id: string;
70
+ bindingKey: string;
71
+ requirementKey: string;
72
+ logicalName: string;
73
+ applicationId: string;
74
+ environmentClass: EnvironmentClass;
75
+ environmentName: string;
76
+ desiredSpecHash: string;
77
+ migrationState: LifecycleMigrationState;
78
+ };
79
+
80
+ // A binding that has completed provisioning. The `ready` discriminant
81
+ // guarantees that the OPA returned both `connectionMetadata` and
82
+ // `credentialRefs`, so consumers can rely on those without null checks.
83
+ export type ReadyDatabaseBinding = DatabaseBindingBase & {
84
+ lifecycleState: 'ready';
85
+ connectionMetadata: Record<string, string | number | boolean>;
86
+ credentialRefs: Record<string, CredentialRef>;
87
+ };
88
+
89
+ // A binding in any non-ready state. Connection metadata and credential refs
90
+ // may be present from a prior `ready` transition or absent if the binding
91
+ // never reached `ready`.
92
+ export type IncompleteDatabaseBinding = DatabaseBindingBase & {
93
+ lifecycleState: Exclude<LifecycleState, 'ready'>;
94
+ connectionMetadata?: Record<string, string | number | boolean>;
95
+ credentialRefs?: Record<string, CredentialRef>;
96
+ };
97
+
98
+ export type DatabaseBinding = ReadyDatabaseBinding | IncompleteDatabaseBinding;
99
+
100
+ export function isReadyDatabaseBinding(binding: DatabaseBinding): binding is ReadyDatabaseBinding {
101
+ return binding.lifecycleState === 'ready';
102
+ }
103
+
104
+ export type LifecycleRequest = {
105
+ id: string;
106
+ operation: LifecycleOperation;
107
+ bindingKey: string;
108
+ requirementKey: string;
109
+ desiredSpecHash: string;
110
+ state: LifecycleState;
111
+ };
112
+
113
+ export type TerraformModuleInput = {
114
+ bindingKey: string;
115
+ requirement: DatabaseRequirement;
116
+ credentialRefs: Record<string, CredentialRef>;
117
+ metadata: Record<string, string>;
118
+ };
119
+
120
+ export type TerraformModuleOutput = {
121
+ connection: Record<string, string | number | boolean>;
122
+ credentialRefs: Record<string, CredentialRef>;
123
+ resourceKey: string;
124
+ };
125
+
126
+ export function computeRequirementKey(requirement: Pick<DatabaseRequirement, 'logicalName' | 'engine'>): string {
127
+ return `${slugify(requirement.logicalName)}~${encodeURIComponent(requirement.logicalName)}:${requirement.engine}`;
128
+ }
129
+
130
+ export function computeBindingKey(input: {
131
+ organizationId: string;
132
+ applicationId: string;
133
+ environmentClass: EnvironmentClass;
134
+ environmentName: string;
135
+ requirementKey: string;
136
+ }): string {
137
+ return [input.organizationId, ...bindingKeySegments(input)].join(':');
138
+ }
139
+
140
+ export function computeLegacyBindingKeyWithoutOrganization(input: {
141
+ applicationId: string;
142
+ environmentClass: EnvironmentClass;
143
+ environmentName: string;
144
+ requirementKey: string;
145
+ }): string {
146
+ return bindingKeySegments(input).join(':');
147
+ }
148
+
149
+ function bindingKeySegments(input: {
150
+ applicationId: string;
151
+ environmentClass: EnvironmentClass;
152
+ environmentName: string;
153
+ requirementKey: string;
154
+ }): string[] {
155
+ return [
156
+ input.applicationId,
157
+ input.environmentClass,
158
+ `${slugify(input.environmentName)}~${encodeURIComponent(input.environmentName)}`,
159
+ input.requirementKey
160
+ ];
161
+ }
162
+
163
+ export async function computeDesiredSpecHash(requirement: DatabaseRequirement): Promise<string> {
164
+ return await sha256Base64(JSON.stringify(canonicalize(requirement)));
165
+ }
166
+
167
+ export function isTerminalLifecycleState(state: LifecycleState): state is LifecycleTerminalState {
168
+ return (LIFECYCLE_TERMINAL_STATES as readonly string[]).includes(state);
169
+ }
170
+
171
+ export function redactLifecycleSecrets<T>(value: T): T {
172
+ return redact(value) as T;
173
+ }
174
+
175
+ function slugify(value: string): string {
176
+ return value
177
+ .trim()
178
+ .toLowerCase()
179
+ .replace(/[^a-z0-9]+/g, '-')
180
+ .replace(/^-|-$/g, '');
181
+ }
182
+
183
+ function canonicalize(value: unknown): unknown {
184
+ if (Array.isArray(value)) {
185
+ return value.map(canonicalize).sort((left, right) => codepointCompare(JSON.stringify(left), JSON.stringify(right)));
186
+ }
187
+ if (value && typeof value === 'object') {
188
+ return Object.fromEntries(
189
+ Object.entries(value)
190
+ .filter(([, entryValue]) => entryValue !== undefined)
191
+ .sort(([left], [right]) => codepointCompare(left, right))
192
+ .map(([key, entryValue]) => [key, canonicalize(entryValue)])
193
+ );
194
+ }
195
+ return value;
196
+ }
197
+
198
+ function codepointCompare(left: string, right: string): number {
199
+ if (left < right) {
200
+ return -1;
201
+ }
202
+ if (left > right) {
203
+ return 1;
204
+ }
205
+ return 0;
206
+ }
207
+
208
+ function redact(value: unknown, preserveCredentialRefs = false): unknown {
209
+ if (Array.isArray(value)) {
210
+ return value.map((entry) => redact(entry, preserveCredentialRefs));
211
+ }
212
+ if (value && typeof value === 'object') {
213
+ return Object.fromEntries(
214
+ Object.entries(value).map(([key, entryValue]) => {
215
+ if (isCredentialRefKey(key)) {
216
+ return [key, redact(entryValue, true)];
217
+ }
218
+ if (!preserveCredentialRefs && SECRET_KEY_PATTERN.test(key)) {
219
+ return [key, '[REDACTED]'];
220
+ }
221
+ return [key, redact(entryValue, preserveCredentialRefs)];
222
+ })
223
+ );
224
+ }
225
+ return value;
226
+ }
227
+
228
+ // Shared secret-key tripwire: keys whose name strongly suggests a raw secret
229
+ // value. Used by `redactLifecycleSecrets` to scrub logs/error reporting and by
230
+ // the server-side `assertNoRawCredentialMaterial` guard to refuse callbacks
231
+ // that violate the typed-CredentialRef contract. NOT a security boundary --
232
+ // callers must still send `Record<string, string | number | boolean>` for
233
+ // `connectionMetadata` and `CredentialRef` for `credentialRefs`; this catches
234
+ // obvious misuse only.
235
+ export const SECRET_KEY_PATTERN = /password|secret|token|private[_-]?key|dsn/i;
236
+
237
+ // Schemes whose URI form embeds credentials before `@`. Used as a value-side
238
+ // tripwire alongside `SECRET_KEY_PATTERN` -- callers should never send
239
+ // connection strings; the typed `connectionMetadata` shape is the contract.
240
+ export const CREDENTIAL_BEARING_DSN_SCHEMES = [
241
+ 'postgres',
242
+ 'postgresql',
243
+ 'mysql',
244
+ 'mariadb',
245
+ 'mongodb',
246
+ 'mongodb+srv',
247
+ 'redis',
248
+ 'rediss',
249
+ 'amqp',
250
+ 'amqps',
251
+ 'kafka'
252
+ ] as const;
253
+
254
+ export function isSecretKey(key: string): boolean {
255
+ return SECRET_KEY_PATTERN.test(key);
256
+ }
257
+
258
+ export function containsCredentialMaterial(value: string): boolean {
259
+ if (DSN_WITH_INLINE_AUTH.test(value)) {
260
+ return true;
261
+ }
262
+ if (CREDENTIAL_KV_PATTERN.test(value)) {
263
+ return true;
264
+ }
265
+ if (JSON_ENCODED_SECRET_PATTERN.test(value)) {
266
+ return true;
267
+ }
268
+ return false;
269
+ }
270
+
271
+ const DSN_SCHEME_GROUP = CREDENTIAL_BEARING_DSN_SCHEMES.join('|').replace(/\+/g, '\\+');
272
+ const DSN_WITH_INLINE_AUTH = new RegExp(`(?:${DSN_SCHEME_GROUP}):\\/\\/[^/@\\s:]+:[^/@\\s]+@`, 'i');
273
+ const CREDENTIAL_KV_PATTERN = /(?:password|pwd|token|secret|private[_-]?key|api[_-]?key)\s*=\s*[^;&\s"]+/i;
274
+ const JSON_ENCODED_SECRET_PATTERN = /["']\s*(?:password|secret|token|private[_-]?key|dsn)\s*["']\s*:/i;
275
+
276
+ function isCredentialRefKey(key: string): boolean {
277
+ return /credential[_-]?ref/i.test(key);
278
+ }
package/src/index.ts CHANGED
@@ -26,3 +26,4 @@ export * from './tracing/methodTracing.js';
26
26
  export * from './tracing/errorSanitizer.js';
27
27
  export * from './jwt/index.js';
28
28
  export * from './org-features/index.js';
29
+ export * from './database-lifecycle/index.js';
@@ -117,6 +117,10 @@ export const CLOUD_PLUGIN_VERSIONS_LATEST: PluginExecutionVersions = {
117
117
  [HubSpotPlugin.id]: MAX_VERSION,
118
118
  [IntercomPlugin.id]: MAX_VERSION,
119
119
  [JavascriptPlugin.id]: MAX_VERSION,
120
+ // The javascriptsdkapi plugin has no corresponding Plugin definition in
121
+ // packages/shared (it's only registered by the orchestrator). Keep the literal
122
+ // string in sync with SDK_API_PLUGIN_ID consumers.
123
+ javascriptsdkapi: MAX_VERSION,
120
124
  [JiraPlugin.id]: MAX_VERSION,
121
125
  [KafkaPlugin.id]: MAX_VERSION,
122
126
  [KinesisPlugin.id]: MAX_VERSION,
@@ -11,9 +11,13 @@ export const DropboxPlugin: Plugin = getNativeOpenApiPlugin({
11
11
  tokenUrl: 'https://www.dropbox.com/oauth2/token',
12
12
  revokeTokenUrl: 'https://api.dropboxapi.com/2/auth/token/revoke',
13
13
  iconUrl: `${PUBLIC_INTEGRATIONS_LOGO_URL}/dropbox.png`,
14
+ defaultScope: 'account_info.read files.metadata.read files.content.read files.content.write',
14
15
  authorizationExtraParams: {
15
16
  stateConfigExclude: ['datasource-auth-state'],
16
- responseType: 'code'
17
+ responseType: 'code',
18
+ additionalUrlParams: {
19
+ token_access_type: 'offline'
20
+ }
17
21
  }
18
22
  }
19
23
  },
@@ -68,6 +68,7 @@ export type AuthorizationExtraParams = {
68
68
  accessType?: string;
69
69
  stateConfigExclude?: AuthorizationStateConfig[];
70
70
  owner?: string;
71
+ additionalUrlParams?: Record<string, string>;
71
72
  };
72
73
 
73
74
  type OAuth2Config = {
@@ -78,6 +79,7 @@ type OAuth2Config = {
78
79
  userInfoUrl?: string;
79
80
  authorizationExtraParams?: AuthorizationExtraParams;
80
81
  iconUrl: string;
82
+ defaultScope?: string;
81
83
  };
82
84
 
83
85
  type OAuth2HostedClientConfig = OAuth2Config & { clientId: string };
@@ -1102,6 +1104,7 @@ export const authSections = ({
1102
1104
  markdownText: `The scope of access request. It may have multiple space-separated values.`
1103
1105
  },
1104
1106
  singleLine: true,
1107
+ initialValue: enabledMethods.oauth2BringYourOwn?.defaultScope ?? enabledMethods.oauth2SuperblocksClient?.defaultScope,
1105
1108
  display: {
1106
1109
  show: {
1107
1110
  authType: [IntegrationAuthType.OAUTH2_CODE]
@@ -121,4 +121,5 @@ export interface AiPromptCostResponse {
121
121
  totalCreditsUsed: number;
122
122
  }
123
123
 
124
+ export * from './provider-credential.js';
124
125
  export * from './quota-paywall.js';
@@ -0,0 +1,61 @@
1
+ export enum AiProviderType {
2
+ SNOWFLAKE_CORTEX = 'snowflake_cortex',
3
+ DATABRICKS_AI_GATEWAY = 'databricks_ai_gateway'
4
+ }
5
+
6
+ export const AI_PROVIDER_CREDENTIAL_STATUSES = ['untested', 'passing', 'failing'] as const;
7
+ export type AiProviderCredentialStatus = (typeof AI_PROVIDER_CREDENTIAL_STATUSES)[number];
8
+
9
+ export interface SnowflakeCortexBYOCredentials {
10
+ account: string;
11
+ username: string;
12
+ role?: string;
13
+ pat: string;
14
+ }
15
+
16
+ export interface DatabricksAiGatewayBYOCredentials {
17
+ gatewayHost: string;
18
+ pat: string;
19
+ }
20
+
21
+ export type BYOCredentials = SnowflakeCortexBYOCredentials | DatabricksAiGatewayBYOCredentials;
22
+
23
+ export interface AiProviderCredentialDto {
24
+ id: string;
25
+ provider: AiProviderType;
26
+ displayName: string;
27
+ providerMetadata?: Record<string, string>;
28
+ status: AiProviderCredentialStatus;
29
+ statusMessage?: string | null;
30
+ statusCheckedAt?: string | null;
31
+ isActive: boolean;
32
+ useManagedFallback: boolean;
33
+ fingerprint: string;
34
+ created: string;
35
+ updated: string;
36
+ }
37
+
38
+ export interface CreateAiProviderCredentialRequest {
39
+ provider: AiProviderType;
40
+ displayName: string;
41
+ credentials: BYOCredentials;
42
+ useManagedFallback?: boolean;
43
+ }
44
+
45
+ export interface UpdateAiProviderCredentialRequest {
46
+ displayName?: string;
47
+ credentials?: Partial<BYOCredentials>;
48
+ isActive?: boolean;
49
+ useManagedFallback?: boolean;
50
+ }
51
+
52
+ export interface TestAiProviderCredentialConfigurationRequest {
53
+ provider: AiProviderType;
54
+ credentials: BYOCredentials;
55
+ }
56
+
57
+ export interface AiProviderCredentialTestResult {
58
+ valid: boolean;
59
+ error?: string;
60
+ checkedAt: string;
61
+ }
@@ -213,7 +213,13 @@ export const AUDIT_EVENT_TYPE_CATALOG: AuditEventTypeEntry[] = [
213
213
  category_uid: OCSFCategoryUid.APPLICATION_ACTIVITY,
214
214
  category_name: 'APPLICATION_ACTIVITY',
215
215
  resource_type: 'Integration',
216
- operations: ['integration.create', 'integration.delete', 'integration.secret.view', 'integration.update']
216
+ operations: [
217
+ 'integration.create',
218
+ 'integration.delete',
219
+ 'integration.ownership.update',
220
+ 'integration.secret.view',
221
+ 'integration.update'
222
+ ]
217
223
  },
218
224
  {
219
225
  class_uid: OCSFClassUid.ENTITY_MANAGEMENT,
@@ -259,7 +265,7 @@ export const AUDIT_EVENT_TYPE_CATALOG: AuditEventTypeEntry[] = [
259
265
  category_uid: OCSFCategoryUid.IAM,
260
266
  category_name: 'IAM',
261
267
  resource_type: 'Token',
262
- operations: ['token.create', 'token.delete', 'token.update']
268
+ operations: ['token.create', 'token.delete', 'token.regenerate', 'token.update']
263
269
  },
264
270
  {
265
271
  class_uid: OCSFClassUid.USER_ACCESS_MANAGEMENT,
@@ -16,6 +16,7 @@ export interface CommitDto {
16
16
  directoryContentsHash?: string | null;
17
17
  buildStatus?: BuildStatus;
18
18
  autosaveType?: string | null;
19
+ templateName?: string | null;
19
20
  }
20
21
 
21
22
  export enum CommitType {
@@ -0,0 +1,27 @@
1
+ import { describe, test, expect } from 'vitest';
2
+
3
+ import { DeploymentTypeEnum, isBrowserTelemetryEnabled } from './env.js';
4
+
5
+ describe('isBrowserTelemetryEnabled', () => {
6
+ test('returns true for CLOUD deployment', () => {
7
+ expect(isBrowserTelemetryEnabled(DeploymentTypeEnum.CLOUD)).toBe(true);
8
+ });
9
+
10
+ test('returns false for CLOUD_PREM deployment', () => {
11
+ expect(isBrowserTelemetryEnabled(DeploymentTypeEnum.CLOUD_PREM)).toBe(false);
12
+ });
13
+
14
+ test('returns false when deploymentType is undefined (fail-closed on ambiguity)', () => {
15
+ expect(isBrowserTelemetryEnabled(undefined)).toBe(false);
16
+ });
17
+
18
+ test("returns false for empty string (helm configmap renders '' when deploymentType override is missing)", () => {
19
+ expect(isBrowserTelemetryEnabled('' as DeploymentTypeEnum)).toBe(false);
20
+ });
21
+
22
+ test('returns false for common typos that would bypass a blocklist check', () => {
23
+ expect(isBrowserTelemetryEnabled('cloud_prem' as DeploymentTypeEnum)).toBe(false);
24
+ expect(isBrowserTelemetryEnabled('CLOUD-PREM' as DeploymentTypeEnum)).toBe(false);
25
+ expect(isBrowserTelemetryEnabled('cloud-prem ' as DeploymentTypeEnum)).toBe(false);
26
+ });
27
+ });
@@ -7,7 +7,35 @@ export enum EnvEnum {
7
7
  LOCAL = 'local'
8
8
  }
9
9
 
10
+ /**
11
+ * Deployment model for the running process.
12
+ *
13
+ * WARNING — opposing defaults by context: `parseDeploymentType` in
14
+ * `@superblocksteam/telemetry/common/deployment-type` (server-side) defaults a MISSING OR
15
+ * EMPTY value to `CLOUD` (fail-open — the server is trusted internal infrastructure) and
16
+ * THROWS on unrecognized values. `isBrowserTelemetryEnabled` in this module (browser-side)
17
+ * fails CLOSED — missing, empty, and unrecognized values all disable telemetry. Both are
18
+ * correct in their contexts (the browser reads values from an untrusted env-var pipeline),
19
+ * but the asymmetry is easy to miss. When adding new deployment-type-gated policies, pick
20
+ * the fail direction deliberately and document it.
21
+ */
10
22
  export enum DeploymentTypeEnum {
11
23
  CLOUD = 'cloud',
12
24
  CLOUD_PREM = 'cloud-prem'
13
25
  }
26
+
27
+ /**
28
+ * Returns whether browser telemetry (Datadog RUM, browser-logs) may be initialized.
29
+ * Allowlist-on-CLOUD: any non-CLOUD value — CLOUD_PREM, undefined, empty string,
30
+ * typos ('cloud_prem', 'CLOUD-PREM'), or future deployment types — disables telemetry.
31
+ * Cloud-prem deployments must not emit to Superblocks' public intake (customer network egress);
32
+ * fail-closed on ambiguity is the correct default for a privacy gate.
33
+ *
34
+ * Parameter widened to `string | undefined` because call sites pull from env.get() with an
35
+ * `as DeploymentTypeEnum` cast — any string can actually arrive (empty, typo, whitespace,
36
+ * unexpected value). The strict equality still narrows correctly; the wider type makes the
37
+ * runtime truth explicit at the boundary.
38
+ */
39
+ export const isBrowserTelemetryEnabled = (deploymentType: string | undefined): boolean => {
40
+ return deploymentType === DeploymentTypeEnum.CLOUD;
41
+ };
@@ -51,11 +51,11 @@ export class ResponseMeta {
51
51
  }
52
52
 
53
53
  export class ErrorDto {
54
- code: number;
54
+ code: number | string;
55
55
  message: string;
56
56
  superblocksError?: SuperblocksError;
57
57
 
58
- constructor({ code, message, type }: { code: number; message: string; type?: SuperblocksError }) {
58
+ constructor({ code, message, type }: { code: number | string; message: string; type?: SuperblocksError }) {
59
59
  this.code = code;
60
60
  this.message = message;
61
61
  this.superblocksError = type;
@@ -100,20 +100,20 @@ export type InferenceSnowflakeCortexPayload = {
100
100
  /**
101
101
  * Databricks AI Gateway credential. AI Gateway only accepts Programmatic
102
102
  * Access Tokens (PATs) as bearer credentials — there is no keypair or
103
- * OAuth path comparable to Snowflake Cortex. `workspaceUrl` is validated
104
- * at the registry with a strict https + known-suffix regex so a typo
105
- * there can't silently point at the wrong workspace at call time.
103
+ * OAuth path comparable to Snowflake Cortex. `gatewayHost` is either the
104
+ * Anthropic-compatible AI Gateway origin or a workspace AI Gateway
105
+ * Anthropic messages URL, both validated at the registry.
106
106
  */
107
107
  export type InferenceDatabricksAiGatewayPayload = {
108
108
  type: 'inference.databricks-ai-gateway.pat';
109
109
  secrets: { pat: string };
110
110
  metadata: {
111
111
  /**
112
- * Full workspace URL, e.g. `https://dbc-abcd1234-5678.cloud.databricks.com`
113
- * or `https://adb-1234567890123456.7.azuredatabricks.net`. Must match
114
- * the strict Databricks-host regex in the registry.
112
+ * AI Gateway origin, e.g. `https://<gateway-name>.ai-gateway.cloud.databricks.com`,
113
+ * or workspace Anthropic messages URL, e.g.
114
+ * `https://<your-databricks-workspace-url>/ai-gateway/anthropic/v1/messages`.
115
115
  */
116
- workspaceUrl: string;
116
+ gatewayHost: string;
117
117
  };
118
118
  };
119
119
 
@@ -22,10 +22,12 @@ export * from './lock/index.js';
22
22
  export * from './observability/index.js';
23
23
  export * from './organization/index.js';
24
24
  export * from './page/index.js';
25
+ export * from './policyGate/index.js';
25
26
  export * from './plugin/index.js';
26
27
  export * from './profile/index.js';
27
28
  export * from './rbac/index.js';
28
29
  export * from './response/index.js';
30
+ export * from './reviewPolicy/index.js';
29
31
  export * from './remoteCommit/index.js';
30
32
  export * from './scim/index.js';
31
33
  export * from './signingKeyRotationJob/index.js';
@@ -89,6 +89,7 @@ class IntegrationBaseDto {
89
89
  name: string;
90
90
  pluginId: string;
91
91
  organizationId: string;
92
+ applicationId?: string | null;
92
93
  isUserConfigured: boolean;
93
94
  demoIntegrationId?: string;
94
95
  }
@@ -125,7 +126,8 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
125
126
  editable,
126
127
  permissions,
127
128
  creator,
128
- enabledForV2
129
+ enabledForV2,
130
+ applicationId
129
131
  }: {
130
132
  id: string;
131
133
  created: Date;
@@ -145,6 +147,7 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
145
147
  permissions?: ActionTypeEnum[];
146
148
  creator?: CreatorDto;
147
149
  enabledForV2?: boolean;
150
+ applicationId?: string | null;
148
151
  }) {
149
152
  super();
150
153
  this.id = id;
@@ -154,6 +157,7 @@ export class SupersetIntegrationDto extends IntegrationBaseDto {
154
157
  this.pluginId = pluginId;
155
158
  this.kind = kind;
156
159
  this.organizationId = organizationId;
160
+ this.applicationId = applicationId;
157
161
  this.configurations = configurations;
158
162
  this.demoIntegrationId = demoIntegrationId;
159
163
  this.isUserConfigured = isUserConfigured;
@@ -202,7 +206,8 @@ export class IntegrationDto extends IntegrationBaseDto {
202
206
  creator,
203
207
  kind,
204
208
  slug,
205
- enabledForV2
209
+ enabledForV2,
210
+ applicationId
206
211
  }: {
207
212
  id: string;
208
213
  created: Date;
@@ -218,6 +223,7 @@ export class IntegrationDto extends IntegrationBaseDto {
218
223
  kind: IntegrationKind;
219
224
  slug?: string;
220
225
  enabledForV2?: boolean;
226
+ applicationId?: string | null;
221
227
  }) {
222
228
  super();
223
229
  this.id = id;
@@ -226,6 +232,7 @@ export class IntegrationDto extends IntegrationBaseDto {
226
232
  this.name = name;
227
233
  this.pluginId = pluginId;
228
234
  this.organizationId = organizationId;
235
+ this.applicationId = applicationId;
229
236
  this.demoIntegrationId = demoIntegrationId;
230
237
  this.configurations = configurations;
231
238
  this.isUserConfigured = isUserConfigured;
@@ -1,4 +1,4 @@
1
- import * as semver from 'semver';
1
+ import semver from 'semver';
2
2
 
3
3
  import { SemVer } from '../plugin/index.js';
4
4
 
@@ -359,6 +359,7 @@ type ConnectOAuthPayload = {
359
359
  responseType?: string;
360
360
  stateConfigExclude?: AuthorizationStateConfig[];
361
361
  owner?: string;
362
+ additionalUrlParams?: Record<string, string>;
362
363
  authType?: AuthType;
363
364
  authConfig?: {
364
365
  clientId: string;