@superblocksteam/sdk 2.0.99 → 2.0.100-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/cli-replacement/dev.d.mts.map +1 -1
- package/dist/cli-replacement/dev.mjs +64 -20
- package/dist/cli-replacement/dev.mjs.map +1 -1
- package/dist/cli-replacement/init.d.ts.map +1 -1
- package/dist/cli-replacement/init.js +1 -0
- package/dist/cli-replacement/init.js.map +1 -1
- package/dist/client.d.ts +8 -21
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +36 -5
- package/dist/client.js.map +1 -1
- package/dist/collect-sdk-apis.d.mts +10 -6
- package/dist/collect-sdk-apis.d.mts.map +1 -1
- package/dist/collect-sdk-apis.mjs +20 -5
- package/dist/collect-sdk-apis.mjs.map +1 -1
- package/dist/collect-sdk-apis.test.mjs +53 -4
- package/dist/collect-sdk-apis.test.mjs.map +1 -1
- package/dist/dbfs/client.d.ts +2 -0
- package/dist/dbfs/client.d.ts.map +1 -1
- package/dist/dbfs/client.js +43 -17
- package/dist/dbfs/client.js.map +1 -1
- package/dist/dbfs/client.test.d.ts +2 -0
- package/dist/dbfs/client.test.d.ts.map +1 -0
- package/dist/dbfs/client.test.js +81 -0
- package/dist/dbfs/client.test.js.map +1 -0
- package/dist/extract-api-integrations.d.mts +17 -0
- package/dist/extract-api-integrations.d.mts.map +1 -0
- package/dist/extract-api-integrations.mjs +233 -0
- package/dist/extract-api-integrations.mjs.map +1 -0
- package/dist/extract-api-integrations.test.d.mts +2 -0
- package/dist/extract-api-integrations.test.d.mts.map +1 -0
- package/dist/extract-api-integrations.test.mjs +97 -0
- package/dist/extract-api-integrations.test.mjs.map +1 -0
- package/dist/sdk.d.ts +13 -6
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +13 -6
- package/dist/sdk.js.map +1 -1
- package/dist/sdk.test.d.ts +2 -0
- package/dist/sdk.test.d.ts.map +1 -0
- package/dist/sdk.test.js +71 -0
- package/dist/sdk.test.js.map +1 -0
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +9 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/logging.d.ts.map +1 -1
- package/dist/telemetry/logging.js +1 -0
- package/dist/telemetry/logging.js.map +1 -1
- package/dist/version-control.d.mts +5 -3
- package/dist/version-control.d.mts.map +1 -1
- package/dist/version-control.mjs +18 -10
- package/dist/version-control.mjs.map +1 -1
- package/dist/vite-plugin-generate-api-build-manifest.d.mts.map +1 -1
- package/dist/vite-plugin-generate-api-build-manifest.mjs.map +1 -1
- package/package.json +6 -6
- package/src/cli-replacement/dev.mts +73 -24
- package/src/cli-replacement/init.ts +1 -0
- package/src/client.ts +53 -22
- package/src/collect-sdk-apis.mts +30 -6
- package/src/collect-sdk-apis.test.mts +58 -4
- package/src/dbfs/client.test.ts +116 -0
- package/src/dbfs/client.ts +60 -21
- package/src/extract-api-integrations.mts +345 -0
- package/src/extract-api-integrations.test.mts +114 -0
- package/src/sdk.test.ts +106 -0
- package/src/sdk.ts +40 -3
- package/src/telemetry/index.ts +13 -2
- package/src/telemetry/logging.ts +1 -0
- package/src/version-control.mts +23 -8
- package/src/vite-plugin-generate-api-build-manifest.mts +7 -1
- package/test/version-control.test.mts +81 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { parse } from "@babel/parser";
|
|
2
|
+
|
|
3
|
+
export type IntegrationInfo = {
|
|
4
|
+
key: string;
|
|
5
|
+
pluginId: string;
|
|
6
|
+
id: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type AstNode = {
|
|
10
|
+
type?: string;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function isNode(value: unknown): value is AstNode {
|
|
15
|
+
return !!value && typeof value === "object";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getPropertyName(node: unknown): string | undefined {
|
|
19
|
+
if (!isNode(node)) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (node.type === "Identifier" && typeof node.name === "string") {
|
|
24
|
+
return node.name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
28
|
+
return node.value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getLiteralString(node: unknown): string | undefined {
|
|
35
|
+
if (!isNode(node)) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
40
|
+
return node.value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (
|
|
44
|
+
node.type === "TemplateLiteral" &&
|
|
45
|
+
Array.isArray(node.expressions) &&
|
|
46
|
+
node.expressions.length === 0 &&
|
|
47
|
+
Array.isArray(node.quasis) &&
|
|
48
|
+
node.quasis.length === 1
|
|
49
|
+
) {
|
|
50
|
+
const quasi = node.quasis[0];
|
|
51
|
+
if (
|
|
52
|
+
isNode(quasi) &&
|
|
53
|
+
isNode(quasi.value) &&
|
|
54
|
+
typeof quasi.value.cooked === "string"
|
|
55
|
+
) {
|
|
56
|
+
return quasi.value.cooked;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getCalleeName(node: unknown): string | undefined {
|
|
64
|
+
if (!isNode(node)) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (node.type === "Identifier" && typeof node.name === "string") {
|
|
69
|
+
return node.name;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (node.type === "MemberExpression") {
|
|
73
|
+
return getPropertyName(node.property);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function unwrapExpression(node: unknown): AstNode | undefined {
|
|
80
|
+
if (!isNode(node)) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (
|
|
85
|
+
node.type === "TSAsExpression" ||
|
|
86
|
+
node.type === "TSTypeAssertion" ||
|
|
87
|
+
node.type === "ParenthesizedExpression"
|
|
88
|
+
) {
|
|
89
|
+
return unwrapExpression(node.expression);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return node;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buildVariableInitializerMap(
|
|
96
|
+
programBody: unknown[],
|
|
97
|
+
): Map<string, unknown> {
|
|
98
|
+
const initializers = new Map<string, unknown>();
|
|
99
|
+
|
|
100
|
+
for (const statement of programBody) {
|
|
101
|
+
if (
|
|
102
|
+
!isNode(statement) ||
|
|
103
|
+
statement.type !== "VariableDeclaration" ||
|
|
104
|
+
!Array.isArray(statement.declarations)
|
|
105
|
+
) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const declaration of statement.declarations) {
|
|
110
|
+
if (!isNode(declaration)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const identifier = declaration.id;
|
|
115
|
+
if (
|
|
116
|
+
!isNode(identifier) ||
|
|
117
|
+
identifier.type !== "Identifier" ||
|
|
118
|
+
typeof identifier.name !== "string"
|
|
119
|
+
) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
initializers.set(identifier.name, declaration.init);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return initializers;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function resolveApiConfigObject(
|
|
131
|
+
expression: unknown,
|
|
132
|
+
variableInitializers: Map<string, unknown>,
|
|
133
|
+
): AstNode | undefined {
|
|
134
|
+
const resolvedExpression = unwrapExpression(expression);
|
|
135
|
+
if (!resolvedExpression) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (
|
|
140
|
+
resolvedExpression.type === "Identifier" &&
|
|
141
|
+
typeof resolvedExpression.name === "string"
|
|
142
|
+
) {
|
|
143
|
+
return resolveApiConfigObject(
|
|
144
|
+
variableInitializers.get(resolvedExpression.name),
|
|
145
|
+
variableInitializers,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
resolvedExpression.type !== "CallExpression" ||
|
|
151
|
+
getCalleeName(resolvedExpression.callee) !== "api" ||
|
|
152
|
+
!Array.isArray(resolvedExpression.arguments)
|
|
153
|
+
) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const config = unwrapExpression(resolvedExpression.arguments[0]);
|
|
158
|
+
if (!config || config.type !== "ObjectExpression") {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return config;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function findExportedApiConfigObject(
|
|
166
|
+
programBody: unknown[],
|
|
167
|
+
): AstNode | undefined {
|
|
168
|
+
const variableInitializers = buildVariableInitializerMap(programBody);
|
|
169
|
+
|
|
170
|
+
for (const statement of programBody) {
|
|
171
|
+
if (!isNode(statement)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (statement.type === "ExportDefaultDeclaration") {
|
|
176
|
+
return resolveApiConfigObject(
|
|
177
|
+
statement.declaration,
|
|
178
|
+
variableInitializers,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
statement.type === "ExportNamedDeclaration" &&
|
|
184
|
+
Array.isArray(statement.specifiers)
|
|
185
|
+
) {
|
|
186
|
+
for (const specifier of statement.specifiers) {
|
|
187
|
+
if (
|
|
188
|
+
!isNode(specifier) ||
|
|
189
|
+
specifier.type !== "ExportSpecifier" ||
|
|
190
|
+
getPropertyName(specifier.exported) !== "default"
|
|
191
|
+
) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return resolveApiConfigObject(specifier.local, variableInitializers);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function buildVariableReferenceMap(
|
|
204
|
+
programBody: unknown[],
|
|
205
|
+
): Map<string, Omit<IntegrationInfo, "key">> {
|
|
206
|
+
const refs = new Map<string, Omit<IntegrationInfo, "key">>();
|
|
207
|
+
|
|
208
|
+
for (const statement of programBody) {
|
|
209
|
+
if (
|
|
210
|
+
!isNode(statement) ||
|
|
211
|
+
statement.type !== "VariableDeclaration" ||
|
|
212
|
+
!Array.isArray(statement.declarations)
|
|
213
|
+
) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for (const declaration of statement.declarations) {
|
|
218
|
+
if (!isNode(declaration)) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const identifier = declaration.id;
|
|
223
|
+
const init = declaration.init;
|
|
224
|
+
if (
|
|
225
|
+
!isNode(identifier) ||
|
|
226
|
+
identifier.type !== "Identifier" ||
|
|
227
|
+
typeof identifier.name !== "string"
|
|
228
|
+
) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (
|
|
233
|
+
!isNode(init) ||
|
|
234
|
+
init.type !== "CallExpression" ||
|
|
235
|
+
!Array.isArray(init.arguments)
|
|
236
|
+
) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const pluginId = getCalleeName(init.callee);
|
|
241
|
+
const id = getLiteralString(init.arguments[0]);
|
|
242
|
+
if (!pluginId || !id) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
refs.set(identifier.name, { pluginId, id });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return refs;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Extract integration declarations from an SDK API source file.
|
|
255
|
+
*
|
|
256
|
+
* Supports both inline declarations:
|
|
257
|
+
* `integrations: { db: postgres("uuid") }`
|
|
258
|
+
*
|
|
259
|
+
* and top-level variable references:
|
|
260
|
+
* `const DB = postgres("uuid"); integrations: { db: DB }`
|
|
261
|
+
*/
|
|
262
|
+
export function extractIntegrationsFromSource(
|
|
263
|
+
source: string,
|
|
264
|
+
): IntegrationInfo[] {
|
|
265
|
+
let ast: ReturnType<typeof parse>;
|
|
266
|
+
try {
|
|
267
|
+
ast = parse(source, {
|
|
268
|
+
sourceType: "module",
|
|
269
|
+
plugins: ["typescript"],
|
|
270
|
+
});
|
|
271
|
+
} catch {
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const variableRefs = buildVariableReferenceMap(ast.program.body);
|
|
276
|
+
const apiConfig = findExportedApiConfigObject(ast.program.body);
|
|
277
|
+
if (!apiConfig) {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
const results: IntegrationInfo[] = [];
|
|
281
|
+
|
|
282
|
+
const apiConfigProperties = Array.isArray(apiConfig.properties)
|
|
283
|
+
? apiConfig.properties
|
|
284
|
+
: [];
|
|
285
|
+
const integrationsProperty = apiConfigProperties.find(
|
|
286
|
+
(property: AstNode) =>
|
|
287
|
+
isNode(property) &&
|
|
288
|
+
property.type === "ObjectProperty" &&
|
|
289
|
+
getPropertyName(property.key) === "integrations",
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
if (
|
|
293
|
+
!isNode(integrationsProperty) ||
|
|
294
|
+
!isNode(integrationsProperty.value) ||
|
|
295
|
+
integrationsProperty.value.type !== "ObjectExpression"
|
|
296
|
+
) {
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const properties = Array.isArray(integrationsProperty.value.properties)
|
|
301
|
+
? integrationsProperty.value.properties
|
|
302
|
+
: [];
|
|
303
|
+
for (const property of properties) {
|
|
304
|
+
if (!isNode(property) || property.type !== "ObjectProperty") {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const key = getPropertyName(property.key);
|
|
309
|
+
if (!key) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (
|
|
314
|
+
isNode(property.value) &&
|
|
315
|
+
property.value.type === "CallExpression" &&
|
|
316
|
+
Array.isArray(property.value.arguments)
|
|
317
|
+
) {
|
|
318
|
+
const pluginId = getCalleeName(property.value.callee);
|
|
319
|
+
const id = getLiteralString(property.value.arguments[0]);
|
|
320
|
+
if (pluginId && id) {
|
|
321
|
+
results.push({ key, pluginId, id });
|
|
322
|
+
}
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (
|
|
327
|
+
isNode(property.value) &&
|
|
328
|
+
property.value.type === "Identifier" &&
|
|
329
|
+
typeof property.value.name === "string"
|
|
330
|
+
) {
|
|
331
|
+
const resolved = variableRefs.get(property.value.name);
|
|
332
|
+
if (resolved) {
|
|
333
|
+
results.push({ key, ...resolved });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return results;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function extractIntegrationIdsFromSource(source: string): string[] {
|
|
342
|
+
return extractIntegrationsFromSource(source).map(
|
|
343
|
+
(integration) => integration.id,
|
|
344
|
+
);
|
|
345
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
extractIntegrationIdsFromSource,
|
|
4
|
+
extractIntegrationsFromSource,
|
|
5
|
+
} from "./extract-api-integrations.mjs";
|
|
6
|
+
|
|
7
|
+
describe("extractIntegrationsFromSource", () => {
|
|
8
|
+
it("extracts integration declarations from API source", () => {
|
|
9
|
+
const source = `
|
|
10
|
+
import { api, postgres, slack } from "@superblocksteam/sdk-api";
|
|
11
|
+
export default api({
|
|
12
|
+
integrations: {
|
|
13
|
+
db: postgres("pg-uuid-prod"),
|
|
14
|
+
notifier: slack("slack-uuid"),
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
expect(extractIntegrationsFromSource(source)).toEqual([
|
|
20
|
+
{ key: "db", pluginId: "postgres", id: "pg-uuid-prod" },
|
|
21
|
+
{ key: "notifier", pluginId: "slack", id: "slack-uuid" },
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("extracts declarations from top-level variable references", () => {
|
|
26
|
+
const source = `
|
|
27
|
+
import { api, postgres } from "@superblocksteam/sdk-api";
|
|
28
|
+
const DB = postgres("db-uuid");
|
|
29
|
+
export default api({
|
|
30
|
+
integrations: { myDb: DB },
|
|
31
|
+
});
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
expect(extractIntegrationsFromSource(source)).toEqual([
|
|
35
|
+
{ key: "myDb", pluginId: "postgres", id: "db-uuid" },
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("supports template literals without expressions", () => {
|
|
40
|
+
const source = `
|
|
41
|
+
import { api, postgres } from "@superblocksteam/sdk-api";
|
|
42
|
+
export default api({
|
|
43
|
+
integrations: { db: postgres(\`pg-uuid\`) },
|
|
44
|
+
});
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
expect(extractIntegrationsFromSource(source)).toEqual([
|
|
48
|
+
{ key: "db", pluginId: "postgres", id: "pg-uuid" },
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("ignores unrelated integrations objects outside the exported API config", () => {
|
|
53
|
+
const source = `
|
|
54
|
+
import { api, postgres, slack } from "@superblocksteam/sdk-api";
|
|
55
|
+
|
|
56
|
+
const helper = {
|
|
57
|
+
integrations: {
|
|
58
|
+
fake: postgres("11111111-1111-1111-1111-111111111111"),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const REAL = slack("22222222-2222-2222-2222-222222222222");
|
|
63
|
+
|
|
64
|
+
export default api({
|
|
65
|
+
integrations: { notifier: REAL },
|
|
66
|
+
async run() {
|
|
67
|
+
return helper.integrations.fake;
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
expect(extractIntegrationsFromSource(source)).toEqual([
|
|
73
|
+
{
|
|
74
|
+
key: "notifier",
|
|
75
|
+
pluginId: "slack",
|
|
76
|
+
id: "22222222-2222-2222-2222-222222222222",
|
|
77
|
+
},
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("returns empty array when no integrations field is present", () => {
|
|
82
|
+
const source = `
|
|
83
|
+
import { api } from "@superblocksteam/sdk-api";
|
|
84
|
+
export default api({});
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
expect(extractIntegrationsFromSource(source)).toEqual([]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("handles malformed source gracefully", () => {
|
|
91
|
+
expect(extractIntegrationsFromSource("this is not valid {{{ code")).toEqual(
|
|
92
|
+
[],
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("extractIntegrationIdsFromSource", () => {
|
|
98
|
+
it("returns the extracted IDs in declaration order", () => {
|
|
99
|
+
const source = `
|
|
100
|
+
import { api, postgres, slack } from "@superblocksteam/sdk-api";
|
|
101
|
+
export default api({
|
|
102
|
+
integrations: {
|
|
103
|
+
db: postgres("pg-uuid-prod"),
|
|
104
|
+
notifier: slack("slack-uuid"),
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
expect(extractIntegrationIdsFromSource(source)).toEqual([
|
|
110
|
+
"pg-uuid-prod",
|
|
111
|
+
"slack-uuid",
|
|
112
|
+
]);
|
|
113
|
+
});
|
|
114
|
+
});
|
package/src/sdk.test.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommitType,
|
|
3
|
+
BillingPlan,
|
|
4
|
+
type GetApplicationCommitsResponseBody,
|
|
5
|
+
type GetPublicOrganizationSummaryResponseBody,
|
|
6
|
+
} from "@superblocksteam/shared";
|
|
7
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
8
|
+
|
|
9
|
+
const { fetchApplicationCommits, fetchOrganizationSummary } = vi.hoisted(
|
|
10
|
+
() => ({
|
|
11
|
+
fetchApplicationCommits: vi.fn(),
|
|
12
|
+
fetchOrganizationSummary: vi.fn(),
|
|
13
|
+
}),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
vi.mock("./client.js", async () => {
|
|
17
|
+
const actual = await vi.importActual("./client.js");
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
...actual,
|
|
21
|
+
fetchApplicationCommits,
|
|
22
|
+
fetchOrganizationSummary,
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
import { SuperblocksSdk } from "./sdk.js";
|
|
27
|
+
|
|
28
|
+
describe("SuperblocksSdk.fetchOrganizationSummary", () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
fetchApplicationCommits.mockReset();
|
|
31
|
+
fetchOrganizationSummary.mockReset();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("delegates to the client helper with the SDK context", async () => {
|
|
35
|
+
const expectedResponse: GetPublicOrganizationSummaryResponseBody = {
|
|
36
|
+
billing_plan: BillingPlan.ENTERPRISE,
|
|
37
|
+
name: "Customer Org",
|
|
38
|
+
organization_id: "org-123",
|
|
39
|
+
organization_type: "opa",
|
|
40
|
+
registered_opas: [
|
|
41
|
+
{
|
|
42
|
+
url: "https://opa.example.com",
|
|
43
|
+
version: "1.2.3",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
fetchOrganizationSummary.mockResolvedValue(expectedResponse);
|
|
48
|
+
|
|
49
|
+
const sdk = new SuperblocksSdk(
|
|
50
|
+
"test-token",
|
|
51
|
+
"https://staging.superblocks.com",
|
|
52
|
+
"1.2.3",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
await expect(sdk.fetchOrganizationSummary("org-123")).resolves.toEqual(
|
|
56
|
+
expectedResponse,
|
|
57
|
+
);
|
|
58
|
+
expect(fetchOrganizationSummary).toHaveBeenCalledWith(
|
|
59
|
+
"1.2.3",
|
|
60
|
+
"test-token",
|
|
61
|
+
"https://staging.superblocks.com",
|
|
62
|
+
"org-123",
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("SuperblocksSdk.fetchApplicationCommits", () => {
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
fetchApplicationCommits.mockReset();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("delegates commit history requests with commit type filters", async () => {
|
|
73
|
+
const expectedResponse: GetApplicationCommitsResponseBody = {
|
|
74
|
+
autosaves: [],
|
|
75
|
+
commits: [],
|
|
76
|
+
};
|
|
77
|
+
fetchApplicationCommits.mockResolvedValue(expectedResponse);
|
|
78
|
+
|
|
79
|
+
const sdk = new SuperblocksSdk(
|
|
80
|
+
"test-token",
|
|
81
|
+
"https://staging.superblocks.com",
|
|
82
|
+
"1.2.3",
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
await expect(
|
|
86
|
+
sdk.fetchApplicationCommits({
|
|
87
|
+
applicationId: "app-123",
|
|
88
|
+
branch: "feature/test",
|
|
89
|
+
commitType: CommitType.ALL,
|
|
90
|
+
limit: 25,
|
|
91
|
+
offset: 50,
|
|
92
|
+
}),
|
|
93
|
+
).resolves.toEqual(expectedResponse);
|
|
94
|
+
expect(fetchApplicationCommits).toHaveBeenCalledWith({
|
|
95
|
+
cliVersion: "1.2.3",
|
|
96
|
+
applicationId: "app-123",
|
|
97
|
+
branch: "feature/test",
|
|
98
|
+
commitType: CommitType.ALL,
|
|
99
|
+
token: "test-token",
|
|
100
|
+
superblocksBaseUrl: "https://staging.superblocks.com",
|
|
101
|
+
injectedHeaders: {},
|
|
102
|
+
limit: 25,
|
|
103
|
+
offset: 50,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
package/src/sdk.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CommitType,
|
|
3
|
+
ExportViewMode,
|
|
4
|
+
type GetPublicOrganizationSummaryResponseBody,
|
|
5
|
+
} from "@superblocksteam/shared";
|
|
2
6
|
import {
|
|
3
7
|
fetchApi,
|
|
4
8
|
fetchApiCommits,
|
|
@@ -9,6 +13,7 @@ import {
|
|
|
9
13
|
fetchApplications,
|
|
10
14
|
fetchApplicationWithComponents,
|
|
11
15
|
fetchCurrentUser,
|
|
16
|
+
fetchOrganizationSummary,
|
|
12
17
|
getApplicationGitConfig,
|
|
13
18
|
getGitFreshToken,
|
|
14
19
|
sync,
|
|
@@ -23,6 +28,7 @@ import {
|
|
|
23
28
|
} from "./client.js";
|
|
24
29
|
import {
|
|
25
30
|
downloadApplicationDirectory,
|
|
31
|
+
downloadDirectoryByHash,
|
|
26
32
|
uploadLocalApplication,
|
|
27
33
|
uploadLocalDirectory,
|
|
28
34
|
printDirectoryEntries,
|
|
@@ -164,12 +170,14 @@ export class SuperblocksSdk {
|
|
|
164
170
|
async fetchApplicationCommits({
|
|
165
171
|
applicationId,
|
|
166
172
|
branch,
|
|
173
|
+
commitType = CommitType.COMMIT,
|
|
167
174
|
headers = {},
|
|
168
175
|
limit,
|
|
169
176
|
offset,
|
|
170
177
|
}: {
|
|
171
178
|
applicationId: string;
|
|
172
179
|
branch: string;
|
|
180
|
+
commitType?: CommitType;
|
|
173
181
|
headers?: Record<string, string>;
|
|
174
182
|
limit?: number;
|
|
175
183
|
offset?: number;
|
|
@@ -178,6 +186,7 @@ export class SuperblocksSdk {
|
|
|
178
186
|
cliVersion: this.cliVersion,
|
|
179
187
|
applicationId,
|
|
180
188
|
branch,
|
|
189
|
+
commitType,
|
|
181
190
|
token: this.token,
|
|
182
191
|
superblocksBaseUrl: this.superblocksBaseUrl,
|
|
183
192
|
injectedHeaders: headers,
|
|
@@ -310,6 +319,17 @@ export class SuperblocksSdk {
|
|
|
310
319
|
);
|
|
311
320
|
}
|
|
312
321
|
|
|
322
|
+
async fetchOrganizationSummary(
|
|
323
|
+
organizationId: string,
|
|
324
|
+
): Promise<GetPublicOrganizationSummaryResponseBody> {
|
|
325
|
+
return fetchOrganizationSummary(
|
|
326
|
+
this.cliVersion,
|
|
327
|
+
this.token,
|
|
328
|
+
this.superblocksBaseUrl,
|
|
329
|
+
organizationId,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
313
333
|
async pushApplication({
|
|
314
334
|
applicationId,
|
|
315
335
|
applicationConfig,
|
|
@@ -405,6 +425,21 @@ export class SuperblocksSdk {
|
|
|
405
425
|
);
|
|
406
426
|
}
|
|
407
427
|
|
|
428
|
+
async dbfsGetDirectoryByHash({
|
|
429
|
+
hash,
|
|
430
|
+
localDirectoryPath,
|
|
431
|
+
}: {
|
|
432
|
+
hash: string;
|
|
433
|
+
localDirectoryPath: string;
|
|
434
|
+
}) {
|
|
435
|
+
await downloadDirectoryByHash(
|
|
436
|
+
this.token,
|
|
437
|
+
this.superblocksBaseUrl,
|
|
438
|
+
hash,
|
|
439
|
+
localDirectoryPath,
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
408
443
|
async dbfsPutApplication({
|
|
409
444
|
applicationId,
|
|
410
445
|
branch,
|
|
@@ -438,10 +473,12 @@ export class SuperblocksSdk {
|
|
|
438
473
|
async dbfsGetApplicationDirectoryHash({
|
|
439
474
|
applicationId,
|
|
440
475
|
branch,
|
|
476
|
+
commitId,
|
|
441
477
|
silent,
|
|
442
478
|
}: {
|
|
443
479
|
applicationId: string;
|
|
444
|
-
branch
|
|
480
|
+
branch?: string;
|
|
481
|
+
commitId?: string;
|
|
445
482
|
silent?: boolean;
|
|
446
483
|
}) {
|
|
447
484
|
return getApplicationDirectoryHash(
|
|
@@ -449,7 +486,7 @@ export class SuperblocksSdk {
|
|
|
449
486
|
this.superblocksBaseUrl,
|
|
450
487
|
applicationId,
|
|
451
488
|
branch,
|
|
452
|
-
{ silent },
|
|
489
|
+
{ commitId, silent },
|
|
453
490
|
);
|
|
454
491
|
}
|
|
455
492
|
|
package/src/telemetry/index.ts
CHANGED
|
@@ -18,8 +18,6 @@ import {
|
|
|
18
18
|
resetTelemetry,
|
|
19
19
|
getDefaultPolicy,
|
|
20
20
|
} from "@superblocksteam/telemetry";
|
|
21
|
-
// Note: dd-trace kept for LLMObs until Epic 07 completes
|
|
22
|
-
import ddTrace from "dd-trace";
|
|
23
21
|
import packageJson from "../../package.json" with { type: "json" };
|
|
24
22
|
import {
|
|
25
23
|
ATTR_SUPERBLOCKS_BASE_URL,
|
|
@@ -33,6 +31,7 @@ import { applyLocalObsEnvVars, isLocalObsEnabled } from "./local-obs.js";
|
|
|
33
31
|
import { getLogger as getStandardLogger } from "./logging.js";
|
|
34
32
|
import { getConfiguration, SERVICE_NAME } from "./util.js";
|
|
35
33
|
import type { ApplicationConfigWithTokenConfigAndUserInfo } from "../types/index.js";
|
|
34
|
+
import type ddTraceDefault from "dd-trace";
|
|
36
35
|
|
|
37
36
|
// Apply local observability env vars early, before dd-trace auto-init
|
|
38
37
|
applyLocalObsEnvVars();
|
|
@@ -41,6 +40,17 @@ applyLocalObsEnvVars();
|
|
|
41
40
|
let config: Awaited<ReturnType<typeof getConfiguration>> | undefined =
|
|
42
41
|
undefined;
|
|
43
42
|
let configured = false;
|
|
43
|
+
type DdTrace = typeof ddTraceDefault;
|
|
44
|
+
|
|
45
|
+
// Load dd-trace only when telemetry is explicitly configured. This keeps
|
|
46
|
+
// lightweight SDK consumers from pulling in dd-trace as a module-load side
|
|
47
|
+
// effect when they only need logger/tracer helpers.
|
|
48
|
+
let ddTracePromise: Promise<DdTrace> | undefined = undefined;
|
|
49
|
+
|
|
50
|
+
async function getDdTrace() {
|
|
51
|
+
ddTracePromise ??= import("dd-trace").then((module) => module.default);
|
|
52
|
+
return await ddTracePromise;
|
|
53
|
+
}
|
|
44
54
|
|
|
45
55
|
/**
|
|
46
56
|
* Current deployment type based on SUPERBLOCKS_DEPLOYMENT_TYPE env var.
|
|
@@ -122,6 +132,7 @@ export async function configureTelemetry(
|
|
|
122
132
|
// NOTE: @joeyagreco - we do this because (per DataDog support):
|
|
123
133
|
// NOTE: @joeyagreco - "There is currently no way to define a retention filter directly for "LLM apps" unless those apps are associated with a specific service tag."
|
|
124
134
|
const SERVICE_NAME_LLM = "superblocks-ai-code-gen";
|
|
135
|
+
const ddTrace = await getDdTrace();
|
|
125
136
|
ddTrace.init({
|
|
126
137
|
env: getEnvironmentFromHostname(config.superblocksHostname),
|
|
127
138
|
service: SERVICE_NAME_LLM,
|