@giselles-ai/sandkit 0.1.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 (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/adapters/drizzle.d.ts +83 -0
  4. package/dist/adapters/drizzle.js +9 -0
  5. package/dist/adapters/drizzle.js.map +1 -0
  6. package/dist/adapters/memory.d.ts +6 -0
  7. package/dist/adapters/memory.js +8 -0
  8. package/dist/adapters/memory.js.map +1 -0
  9. package/dist/adapters/sqlite-bun.d.ts +7 -0
  10. package/dist/adapters/sqlite-bun.js +8 -0
  11. package/dist/adapters/sqlite-bun.js.map +1 -0
  12. package/dist/bin.js +697 -0
  13. package/dist/bin.js.map +1 -0
  14. package/dist/chunk-7DLK7LOM.js +44 -0
  15. package/dist/chunk-7DLK7LOM.js.map +1 -0
  16. package/dist/chunk-BDPTYR6V.js +407 -0
  17. package/dist/chunk-BDPTYR6V.js.map +1 -0
  18. package/dist/chunk-CSOBTLWV.js +202 -0
  19. package/dist/chunk-CSOBTLWV.js.map +1 -0
  20. package/dist/chunk-DLGUA3H7.js +9 -0
  21. package/dist/chunk-DLGUA3H7.js.map +1 -0
  22. package/dist/chunk-FSDVHEEX.js +45 -0
  23. package/dist/chunk-FSDVHEEX.js.map +1 -0
  24. package/dist/chunk-HVYCAAZQ.js +25 -0
  25. package/dist/chunk-HVYCAAZQ.js.map +1 -0
  26. package/dist/chunk-LC3IYBAL.js +100 -0
  27. package/dist/chunk-LC3IYBAL.js.map +1 -0
  28. package/dist/chunk-REGOUXVI.js +58 -0
  29. package/dist/chunk-REGOUXVI.js.map +1 -0
  30. package/dist/chunk-RMMOQD5Y.js +211 -0
  31. package/dist/chunk-RMMOQD5Y.js.map +1 -0
  32. package/dist/chunk-UDFWES6J.js +486 -0
  33. package/dist/chunk-UDFWES6J.js.map +1 -0
  34. package/dist/chunk-VISDS5T7.js +202 -0
  35. package/dist/chunk-VISDS5T7.js.map +1 -0
  36. package/dist/chunk-XM4HGRXW.js +37 -0
  37. package/dist/chunk-XM4HGRXW.js.map +1 -0
  38. package/dist/cli/index.d.ts +19 -0
  39. package/dist/cli/index.js +397 -0
  40. package/dist/cli/index.js.map +1 -0
  41. package/dist/index.d.ts +78 -0
  42. package/dist/index.js +1102 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/integrations/mock.d.ts +19 -0
  45. package/dist/integrations/mock.js +207 -0
  46. package/dist/integrations/mock.js.map +1 -0
  47. package/dist/integrations/vercel.d.ts +7 -0
  48. package/dist/integrations/vercel.js +400 -0
  49. package/dist/integrations/vercel.js.map +1 -0
  50. package/dist/policies/ai-gateway.d.ts +15 -0
  51. package/dist/policies/ai-gateway.js +12 -0
  52. package/dist/policies/ai-gateway.js.map +1 -0
  53. package/dist/policies/codex.d.ts +10 -0
  54. package/dist/policies/codex.js +12 -0
  55. package/dist/policies/codex.js.map +1 -0
  56. package/dist/policies/gemini.d.ts +10 -0
  57. package/dist/policies/gemini.js +12 -0
  58. package/dist/policies/gemini.js.map +1 -0
  59. package/dist/schema/index.d.ts +60 -0
  60. package/dist/schema/index.js +31 -0
  61. package/dist/schema/index.js.map +1 -0
  62. package/dist/types-BCgprbo8.d.ts +47 -0
  63. package/dist/types-BEKQnjeb.d.ts +139 -0
  64. package/dist/types-Cy36bS1j.d.ts +138 -0
  65. package/package.json +126 -0
@@ -0,0 +1,202 @@
1
+ // src/policies/dsl.ts
2
+ function isRecord(value) {
3
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4
+ }
5
+ function isStringArray(value) {
6
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
7
+ }
8
+ function isRecordArray(value) {
9
+ return Array.isArray(value) && value.every((entry) => isRecord(entry));
10
+ }
11
+ function normalizeCredentialSource(source) {
12
+ if (source.kind === "default") {
13
+ return { kind: "default" };
14
+ }
15
+ if (source.kind === "value") {
16
+ if (!source.value) {
17
+ throw new Error("Policy credential value must not be empty.");
18
+ }
19
+ return { kind: "value", value: source.value };
20
+ }
21
+ return { kind: "redacted" };
22
+ }
23
+ function normalizeHeaderTransform(header) {
24
+ const headerName = header.headerName.trim().toLowerCase();
25
+ if (!headerName) {
26
+ throw new Error("Policy header transform must include a header name.");
27
+ }
28
+ return {
29
+ headerName,
30
+ valuePrefix: header.valuePrefix,
31
+ credential: normalizeCredentialSource(header.credential)
32
+ };
33
+ }
34
+ function normalizeService(service) {
35
+ const id = service.id.trim();
36
+ const name = service.name.trim();
37
+ const domains = [
38
+ ...new Set(service.domains.map((domain) => domain.trim()).filter(Boolean))
39
+ ].sort();
40
+ if (!id) {
41
+ throw new Error("Policy service id must not be empty.");
42
+ }
43
+ if (!name) {
44
+ throw new Error(`Policy service "${id}" must have a name.`);
45
+ }
46
+ if (domains.length === 0) {
47
+ throw new Error(`Policy service "${id}" must declare at least one domain.`);
48
+ }
49
+ return {
50
+ id,
51
+ name,
52
+ description: service.description?.trim() || void 0,
53
+ domains,
54
+ headers: service.headers?.map((header) => normalizeHeaderTransform(header))
55
+ };
56
+ }
57
+ function normalizeServices(services) {
58
+ if (services.length === 0) {
59
+ throw new Error("allowServices(...) requires at least one service descriptor.");
60
+ }
61
+ const deduped = /* @__PURE__ */ new Map();
62
+ for (const service of services) {
63
+ const normalized = normalizeService(service);
64
+ deduped.set(normalized.id, normalized);
65
+ }
66
+ return [...deduped.values()].sort((left, right) => left.id.localeCompare(right.id));
67
+ }
68
+ function allowAll() {
69
+ return { mode: "allow-all" };
70
+ }
71
+ function denyAll() {
72
+ return { mode: "deny-all" };
73
+ }
74
+ function allowService(service) {
75
+ return allowServices([service]);
76
+ }
77
+ function allowServices(services) {
78
+ return {
79
+ mode: "allow-services",
80
+ services: normalizeServices(services)
81
+ };
82
+ }
83
+ function describeWorkspacePolicy(policy) {
84
+ switch (policy.mode) {
85
+ case "allow-all":
86
+ return "allow-all";
87
+ case "deny-all":
88
+ return "deny-all";
89
+ case "allow-services":
90
+ return `allow-services:${policy.services.map((service) => service.id).join(",")}`;
91
+ }
92
+ }
93
+ function serializeWorkspacePolicy(policy) {
94
+ return policy;
95
+ }
96
+ function redactWorkspacePolicy(policy) {
97
+ if (policy.mode !== "allow-services") {
98
+ return policy;
99
+ }
100
+ return {
101
+ mode: "allow-services",
102
+ services: policy.services.map((service) => ({
103
+ ...service,
104
+ headers: service.headers?.map((header) => ({
105
+ headerName: header.headerName,
106
+ valuePrefix: header.valuePrefix,
107
+ credential: header.credential.kind === "value" ? { kind: "redacted" } : header.credential
108
+ }))
109
+ }))
110
+ };
111
+ }
112
+ function assertWorkspacePolicyIsDurable(policy) {
113
+ if (policy.mode !== "allow-services") {
114
+ return;
115
+ }
116
+ for (const service of policy.services) {
117
+ for (const header of service.headers ?? []) {
118
+ if (header.credential.kind === "value") {
119
+ throw new Error(
120
+ `Workspace policy for service "${service.id}" contains an explicit secret and cannot be stored durably.`
121
+ );
122
+ }
123
+ }
124
+ }
125
+ }
126
+ function parseWorkspacePolicy(value) {
127
+ if (!isRecord(value) || typeof value.mode !== "string") {
128
+ throw new Error("expected workspace policy object");
129
+ }
130
+ if (value.mode === "allow-all") {
131
+ return allowAll();
132
+ }
133
+ if (value.mode === "deny-all") {
134
+ return denyAll();
135
+ }
136
+ if (value.mode === "allow-services") {
137
+ if (!Array.isArray(value.services)) {
138
+ throw new Error("allow-services policy must include a services array");
139
+ }
140
+ const services = value.services.map((service, index) => {
141
+ if (!isRecord(service)) {
142
+ throw new Error(`service at index ${index} must be an object`);
143
+ }
144
+ if (typeof service.id !== "string" || typeof service.name !== "string" || !isStringArray(service.domains)) {
145
+ throw new Error(`service at index ${index} has an invalid shape`);
146
+ }
147
+ const headers = service.headers === void 0 ? void 0 : (() => {
148
+ if (!isRecordArray(service.headers)) {
149
+ throw new Error(`service at index ${index} has invalid headers`);
150
+ }
151
+ return service.headers.map((header, headerIndex) => {
152
+ if (typeof header.headerName !== "string" || !isRecord(header.credential) || typeof header.credential.kind !== "string") {
153
+ throw new Error(
154
+ `service at index ${index} has invalid header transform at ${headerIndex}`
155
+ );
156
+ }
157
+ if (header.credential.kind === "default") {
158
+ return normalizeHeaderTransform({
159
+ headerName: header.headerName,
160
+ valuePrefix: typeof header.valuePrefix === "string" ? header.valuePrefix : void 0,
161
+ credential: {
162
+ kind: "default"
163
+ }
164
+ });
165
+ }
166
+ if (header.credential.kind === "redacted") {
167
+ return normalizeHeaderTransform({
168
+ headerName: header.headerName,
169
+ valuePrefix: typeof header.valuePrefix === "string" ? header.valuePrefix : void 0,
170
+ credential: { kind: "redacted" }
171
+ });
172
+ }
173
+ throw new Error(
174
+ `service at index ${index} contains a non-durable credential source`
175
+ );
176
+ });
177
+ })();
178
+ return normalizeService({
179
+ id: service.id,
180
+ name: service.name,
181
+ description: typeof service.description === "string" ? service.description : void 0,
182
+ domains: service.domains,
183
+ headers
184
+ });
185
+ });
186
+ return allowServices(services);
187
+ }
188
+ throw new Error(`unsupported workspace policy mode "${value.mode}"`);
189
+ }
190
+
191
+ export {
192
+ allowAll,
193
+ denyAll,
194
+ allowService,
195
+ allowServices,
196
+ describeWorkspacePolicy,
197
+ serializeWorkspacePolicy,
198
+ redactWorkspacePolicy,
199
+ assertWorkspacePolicyIsDurable,
200
+ parseWorkspacePolicy
201
+ };
202
+ //# sourceMappingURL=chunk-VISDS5T7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/policies/dsl.ts"],"sourcesContent":["import type { JsonValue } from \"../types.ts\";\nimport type {\n PolicyServiceCredentialSource,\n PolicyServiceDescriptor,\n PolicyServiceHeaderTransform,\n WorkspacePolicy,\n} from \"./types.ts\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((entry) => typeof entry === \"string\");\n}\n\nfunction isRecordArray(value: unknown): value is Record<string, unknown>[] {\n return Array.isArray(value) && value.every((entry) => isRecord(entry));\n}\n\nfunction normalizeCredentialSource(\n source: PolicyServiceCredentialSource,\n): PolicyServiceCredentialSource {\n if (source.kind === \"default\") {\n return { kind: \"default\" };\n }\n\n if (source.kind === \"value\") {\n if (!source.value) {\n throw new Error(\"Policy credential value must not be empty.\");\n }\n return { kind: \"value\", value: source.value };\n }\n\n return { kind: \"redacted\" };\n}\n\nfunction normalizeHeaderTransform(\n header: PolicyServiceHeaderTransform,\n): PolicyServiceHeaderTransform {\n const headerName = header.headerName.trim().toLowerCase();\n if (!headerName) {\n throw new Error(\"Policy header transform must include a header name.\");\n }\n\n return {\n headerName,\n valuePrefix: header.valuePrefix,\n credential: normalizeCredentialSource(header.credential),\n };\n}\n\nfunction normalizeService(service: PolicyServiceDescriptor): PolicyServiceDescriptor {\n const id = service.id.trim();\n const name = service.name.trim();\n const domains = [\n ...new Set(service.domains.map((domain) => domain.trim()).filter(Boolean)),\n ].sort();\n\n if (!id) {\n throw new Error(\"Policy service id must not be empty.\");\n }\n\n if (!name) {\n throw new Error(`Policy service \"${id}\" must have a name.`);\n }\n\n if (domains.length === 0) {\n throw new Error(`Policy service \"${id}\" must declare at least one domain.`);\n }\n\n return {\n id,\n name,\n description: service.description?.trim() || undefined,\n domains,\n headers: service.headers?.map((header) => normalizeHeaderTransform(header)),\n };\n}\n\nfunction normalizeServices(\n services: readonly PolicyServiceDescriptor[],\n): readonly PolicyServiceDescriptor[] {\n if (services.length === 0) {\n throw new Error(\"allowServices(...) requires at least one service descriptor.\");\n }\n\n const deduped = new Map<string, PolicyServiceDescriptor>();\n for (const service of services) {\n const normalized = normalizeService(service);\n deduped.set(normalized.id, normalized);\n }\n\n return [...deduped.values()].sort((left, right) => left.id.localeCompare(right.id));\n}\n\nexport function allowAll(): WorkspacePolicy {\n return { mode: \"allow-all\" };\n}\n\nexport function denyAll(): WorkspacePolicy {\n return { mode: \"deny-all\" };\n}\n\nexport function allowService(service: PolicyServiceDescriptor): WorkspacePolicy {\n return allowServices([service]);\n}\n\nexport function allowServices(services: readonly PolicyServiceDescriptor[]): WorkspacePolicy {\n return {\n mode: \"allow-services\",\n services: normalizeServices(services),\n };\n}\n\nexport function describeWorkspacePolicy(policy: WorkspacePolicy): string {\n switch (policy.mode) {\n case \"allow-all\":\n return \"allow-all\";\n case \"deny-all\":\n return \"deny-all\";\n case \"allow-services\":\n return `allow-services:${policy.services.map((service) => service.id).join(\",\")}`;\n }\n}\n\nexport function serializeWorkspacePolicy(policy: WorkspacePolicy): JsonValue {\n return policy as unknown as JsonValue;\n}\n\nexport function redactWorkspacePolicy(policy: WorkspacePolicy): WorkspacePolicy {\n if (policy.mode !== \"allow-services\") {\n return policy;\n }\n\n return {\n mode: \"allow-services\",\n services: policy.services.map((service) => ({\n ...service,\n headers: service.headers?.map((header) => ({\n headerName: header.headerName,\n valuePrefix: header.valuePrefix,\n credential: header.credential.kind === \"value\" ? { kind: \"redacted\" } : header.credential,\n })),\n })),\n };\n}\n\nexport function assertWorkspacePolicyIsDurable(policy: WorkspacePolicy): void {\n if (policy.mode !== \"allow-services\") {\n return;\n }\n\n for (const service of policy.services) {\n for (const header of service.headers ?? []) {\n if (header.credential.kind === \"value\") {\n throw new Error(\n `Workspace policy for service \"${service.id}\" contains an explicit secret and cannot be stored durably.`,\n );\n }\n }\n }\n}\n\nexport function parseWorkspacePolicy(value: unknown): WorkspacePolicy {\n if (!isRecord(value) || typeof value.mode !== \"string\") {\n throw new Error(\"expected workspace policy object\");\n }\n\n if (value.mode === \"allow-all\") {\n return allowAll();\n }\n\n if (value.mode === \"deny-all\") {\n return denyAll();\n }\n\n if (value.mode === \"allow-services\") {\n if (!Array.isArray(value.services)) {\n throw new Error(\"allow-services policy must include a services array\");\n }\n\n const services = value.services.map((service, index) => {\n if (!isRecord(service)) {\n throw new Error(`service at index ${index} must be an object`);\n }\n\n if (\n typeof service.id !== \"string\" ||\n typeof service.name !== \"string\" ||\n !isStringArray(service.domains)\n ) {\n throw new Error(`service at index ${index} has an invalid shape`);\n }\n\n const headers =\n service.headers === undefined\n ? undefined\n : (() => {\n if (!isRecordArray(service.headers)) {\n throw new Error(`service at index ${index} has invalid headers`);\n }\n\n return service.headers.map((header, headerIndex) => {\n if (\n typeof header.headerName !== \"string\" ||\n !isRecord(header.credential) ||\n typeof header.credential.kind !== \"string\"\n ) {\n throw new Error(\n `service at index ${index} has invalid header transform at ${headerIndex}`,\n );\n }\n\n if (header.credential.kind === \"default\") {\n return normalizeHeaderTransform({\n headerName: header.headerName,\n valuePrefix:\n typeof header.valuePrefix === \"string\" ? header.valuePrefix : undefined,\n credential: {\n kind: \"default\",\n },\n });\n }\n\n if (header.credential.kind === \"redacted\") {\n return normalizeHeaderTransform({\n headerName: header.headerName,\n valuePrefix:\n typeof header.valuePrefix === \"string\" ? header.valuePrefix : undefined,\n credential: { kind: \"redacted\" },\n });\n }\n\n throw new Error(\n `service at index ${index} contains a non-durable credential source`,\n );\n });\n })();\n\n return normalizeService({\n id: service.id,\n name: service.name,\n description: typeof service.description === \"string\" ? service.description : undefined,\n domains: service.domains,\n headers,\n });\n });\n\n return allowServices(services);\n }\n\n throw new Error(`unsupported workspace policy mode \"${value.mode}\"`);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,37 @@
1
+ // src/policies/github.ts
2
+ var GITHUB_DOMAINS = ["github.com", "*.github.com", "api.github.com", "*.githubusercontent.com"];
3
+ function resolveGithubCredential(options) {
4
+ if (options === void 0) {
5
+ return { kind: "default" };
6
+ }
7
+ if (typeof options.apiKey !== "string" || options.apiKey.trim().length === 0) {
8
+ throw new Error(
9
+ 'github(...) explicit override requires a non-empty "apiKey". Omit the options object to use GITHUB_TOKEN.'
10
+ );
11
+ }
12
+ return { kind: "value", value: options.apiKey };
13
+ }
14
+ function resolveGithubDefaultApiKey() {
15
+ return process.env.GITHUB_TOKEN;
16
+ }
17
+ function github(options) {
18
+ return {
19
+ id: "github",
20
+ name: "GitHub",
21
+ description: "Allow outbound access commonly needed for GitHub APIs and assets.",
22
+ domains: GITHUB_DOMAINS,
23
+ headers: [
24
+ {
25
+ headerName: "authorization",
26
+ valuePrefix: "Bearer ",
27
+ credential: resolveGithubCredential(options)
28
+ }
29
+ ]
30
+ };
31
+ }
32
+
33
+ export {
34
+ resolveGithubDefaultApiKey,
35
+ github
36
+ };
37
+ //# sourceMappingURL=chunk-XM4HGRXW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/policies/github.ts"],"sourcesContent":["import type { PolicyServiceDescriptor } from \"./types.ts\";\n\nconst GITHUB_DOMAINS = [\"github.com\", \"*.github.com\", \"api.github.com\", \"*.githubusercontent.com\"];\n\nexport interface GithubOptions {\n readonly apiKey?: string;\n}\n\nfunction resolveGithubCredential(options?: GithubOptions) {\n if (options === undefined) {\n return { kind: \"default\" } as const;\n }\n\n if (typeof options.apiKey !== \"string\" || options.apiKey.trim().length === 0) {\n throw new Error(\n 'github(...) explicit override requires a non-empty \"apiKey\". Omit the options object to use GITHUB_TOKEN.',\n );\n }\n\n return { kind: \"value\", value: options.apiKey } as const;\n}\n\nexport function resolveGithubDefaultApiKey(): string | undefined {\n return process.env.GITHUB_TOKEN;\n}\n\nexport function github(options?: GithubOptions): PolicyServiceDescriptor {\n return {\n id: \"github\",\n name: \"GitHub\",\n description: \"Allow outbound access commonly needed for GitHub APIs and assets.\",\n domains: GITHUB_DOMAINS,\n headers: [\n {\n headerName: \"authorization\",\n valuePrefix: \"Bearer \",\n credential: resolveGithubCredential(options),\n },\n ],\n };\n}\n"],"mappings":";AAEA,IAAM,iBAAiB,CAAC,cAAc,gBAAgB,kBAAkB,yBAAyB;AAMjG,SAAS,wBAAwB,SAAyB;AACxD,MAAI,YAAY,QAAW;AACzB,WAAO,EAAE,MAAM,UAAU;AAAA,EAC3B;AAEA,MAAI,OAAO,QAAQ,WAAW,YAAY,QAAQ,OAAO,KAAK,EAAE,WAAW,GAAG;AAC5E,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,SAAS,OAAO,QAAQ,OAAO;AAChD;AAEO,SAAS,6BAAiD;AAC/D,SAAO,QAAQ,IAAI;AACrB;AAEO,SAAS,OAAO,SAAkD;AACvE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS;AAAA,MACP;AAAA,QACE,YAAY;AAAA,QACZ,aAAa;AAAA,QACb,YAAY,wBAAwB,OAAO;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,19 @@
1
+ interface SandkitGenerateResult {
2
+ command: "generate";
3
+ provider: "sqlite" | "postgresql" | "mysql";
4
+ outputTarget: "stdout" | "file";
5
+ outputFile: string | null;
6
+ payload: {
7
+ generatedAt: string;
8
+ summary: {
9
+ tableCount: number;
10
+ tableNames: string[];
11
+ };
12
+ schemaText: string;
13
+ };
14
+ }
15
+
16
+ type SandkitRunResult = SandkitGenerateResult;
17
+ declare function runCli(argv?: string[]): Promise<SandkitRunResult>;
18
+
19
+ export { type SandkitRunResult, runCli };
@@ -0,0 +1,397 @@
1
+ import {
2
+ createGeneratePayload
3
+ } from "../chunk-LC3IYBAL.js";
4
+ import {
5
+ createModelSnapshot
6
+ } from "../chunk-RMMOQD5Y.js";
7
+
8
+ // src/cli/generate.ts
9
+ import { mkdirSync, writeFileSync } from "fs";
10
+ import { dirname } from "path";
11
+
12
+ // src/cli/discovery.ts
13
+ import { existsSync, readFileSync } from "fs";
14
+ import { join } from "path";
15
+ import { createInterface } from "readline";
16
+ var drizzleConfigFiles = [
17
+ "drizzle.config.ts",
18
+ "drizzle.config.js",
19
+ "drizzle.config.mjs",
20
+ "drizzle.config.cjs",
21
+ "drizzle.config.mts",
22
+ "drizzle.config.cts",
23
+ "drizzle.config.json",
24
+ "drizzle.config.yaml",
25
+ "drizzle.config.yml"
26
+ ];
27
+ var drizzleDependency = "drizzle-orm";
28
+ function parsePackageJsonDependencies(packagePath, evidence) {
29
+ if (!existsSync(packagePath)) {
30
+ return [];
31
+ }
32
+ let packageData = {};
33
+ try {
34
+ packageData = JSON.parse(readFileSync(packagePath, "utf8"));
35
+ } catch {
36
+ return [];
37
+ }
38
+ const dependencies = { ...packageData.dependencies, ...packageData.devDependencies };
39
+ const depNames = Object.keys(dependencies);
40
+ if (!depNames.includes(drizzleDependency)) {
41
+ return [];
42
+ }
43
+ evidence.push({
44
+ source: "package.json",
45
+ signal: "Dependency `drizzle-orm` found.",
46
+ score: 1.1
47
+ });
48
+ const sqliteSignals = [
49
+ ["better-sqlite3", "sqlite"],
50
+ ["libsql", "sqlite"],
51
+ ["@libsql/client", "sqlite"],
52
+ ["@turso", "sqlite"]
53
+ ].flatMap(
54
+ ([name, provider]) => depNames.some((dep) => dep.includes(name)) ? [{ provider, weight: 1.6 }] : []
55
+ );
56
+ const postgresSignals = [
57
+ ["pg", "postgresql"],
58
+ ["postgres", "postgresql"],
59
+ ["@vercel/postgres", "postgresql"],
60
+ ["@neondatabase/serverless", "postgresql"]
61
+ ].flatMap(
62
+ ([name, provider]) => depNames.some((dep) => dep.includes(name)) ? [{ provider, weight: 1.6 }] : []
63
+ );
64
+ const addSignals = (signals) => signals.forEach(
65
+ (entry) => evidence.push({
66
+ source: "package.json",
67
+ signal: `Dependency implies ${entry.provider} driver usage (${entry.provider} signal).`,
68
+ provider: entry.provider,
69
+ score: entry.weight
70
+ })
71
+ );
72
+ addSignals(sqliteSignals);
73
+ addSignals(postgresSignals);
74
+ return [...sqliteSignals, ...postgresSignals];
75
+ }
76
+ function inspectDrizzleConfig(cwd, evidence) {
77
+ const results = [];
78
+ for (const filename of drizzleConfigFiles) {
79
+ const configPath = join(cwd, filename);
80
+ if (!existsSync(configPath)) {
81
+ continue;
82
+ }
83
+ let text = "";
84
+ try {
85
+ text = readFileSync(configPath, "utf8");
86
+ } catch {
87
+ continue;
88
+ }
89
+ evidence.push({
90
+ source: filename,
91
+ signal: `Found drizzle config file: ${filename}.`,
92
+ score: 0.6
93
+ });
94
+ if (/dialect\s*:\s*["']sqlite["']/i.test(text)) {
95
+ evidence.push({
96
+ source: filename,
97
+ signal: "Drizzle config sets dialect to sqlite.",
98
+ provider: "sqlite",
99
+ score: 3
100
+ });
101
+ return [{ provider: "sqlite", weight: 3 }];
102
+ }
103
+ if (/dialect\s*:\s*["']postgresql["']/i.test(text)) {
104
+ evidence.push({
105
+ source: filename,
106
+ signal: "Drizzle config sets dialect to postgresql.",
107
+ provider: "postgresql",
108
+ score: 3
109
+ });
110
+ return [{ provider: "postgresql", weight: 3 }];
111
+ }
112
+ if (/dialect\s*:\s*["']mysql["']/i.test(text)) {
113
+ evidence.push({
114
+ source: filename,
115
+ signal: "Drizzle config sets dialect to mysql.",
116
+ provider: "mysql",
117
+ score: 3
118
+ });
119
+ return [{ provider: "mysql", weight: 3 }];
120
+ }
121
+ const dbUrlMatch = text.match(/url\s*:\s*["'`](.*?)["'`]/i);
122
+ if (dbUrlMatch?.[1]) {
123
+ const url = dbUrlMatch[1].toLowerCase();
124
+ if (url.includes("turso") || url.includes("libsql") || url.includes("sqlite")) {
125
+ evidence.push({
126
+ source: filename,
127
+ signal: "Drizzle config DB URL contains sqlite/libsql/turso hints.",
128
+ provider: "sqlite",
129
+ score: 2
130
+ });
131
+ results.push({ provider: "sqlite", weight: 2 });
132
+ }
133
+ if (url.includes("postgres")) {
134
+ evidence.push({
135
+ source: filename,
136
+ signal: "Drizzle config DB URL contains postgres hints.",
137
+ provider: "postgresql",
138
+ score: 2
139
+ });
140
+ results.push({ provider: "postgresql", weight: 2 });
141
+ }
142
+ }
143
+ }
144
+ return results;
145
+ }
146
+ function collectEvidence(cwd) {
147
+ const evidence = [];
148
+ const packageSignals = parsePackageJsonDependencies(join(cwd, "package.json"), evidence);
149
+ const drizzleSignals = inspectDrizzleConfig(cwd, evidence);
150
+ return {
151
+ evidence,
152
+ isDrizzleProject: packageSignals.length > 0 || drizzleSignals.length > 0
153
+ };
154
+ }
155
+ function rankProviderSignals(evidence) {
156
+ const totals = { sqlite: 0, postgresql: 0, mysql: 0 };
157
+ for (const item of evidence) {
158
+ if (!item.provider) continue;
159
+ totals[item.provider] += item.score;
160
+ }
161
+ const ranked = Object.entries(totals).sort((a, b) => b[1] - a[1]).filter((entry) => entry[1] > 0);
162
+ if (ranked.length === 0) {
163
+ return { provider: void 0, confidence: 0 };
164
+ }
165
+ const [winner, winnerScore] = ranked[0];
166
+ const secondScore = ranked[1]?.[1] ?? 0;
167
+ const confidence = winnerScore === 0 ? 0 : winnerScore / (winnerScore + secondScore);
168
+ return { provider: winner, confidence };
169
+ }
170
+ function promptWithChoices(question, options, fallback) {
171
+ return new Promise((resolve) => {
172
+ const rl = createInterface({
173
+ input: process.stdin,
174
+ output: process.stdout
175
+ });
176
+ rl.question(question, (answer) => {
177
+ rl.close();
178
+ const normalized = answer.trim().toLowerCase();
179
+ if (options.includes(normalized)) {
180
+ resolve(normalized);
181
+ return;
182
+ }
183
+ resolve(fallback);
184
+ });
185
+ });
186
+ }
187
+ async function resolveProviderWithDiscovery(options, cwd = process.cwd()) {
188
+ const requestedProvider = options.provider ?? options.dialect;
189
+ const normalizedProvider = requestedProvider === "pg" ? "postgresql" : requestedProvider;
190
+ if (normalizedProvider) {
191
+ if (normalizedProvider !== "sqlite" && normalizedProvider !== "postgresql" && normalizedProvider !== "mysql") {
192
+ throw new Error("Invalid provider. Use one of: sqlite, postgresql, mysql.");
193
+ }
194
+ return {
195
+ adapter: "drizzle",
196
+ provider: normalizedProvider,
197
+ confidence: 1,
198
+ evidence: [
199
+ {
200
+ source: "cli",
201
+ signal: `Provider explicitly set as ${normalizedProvider}.`,
202
+ provider: normalizedProvider,
203
+ score: 100
204
+ }
205
+ ],
206
+ prompted: false
207
+ };
208
+ }
209
+ if (options.adapter && options.adapter !== "drizzle") {
210
+ throw new Error("Unsupported adapter. This release supports --adapter drizzle only.");
211
+ }
212
+ const { evidence, isDrizzleProject } = collectEvidence(cwd);
213
+ const inference = rankProviderSignals(evidence);
214
+ const adapter = isDrizzleProject ? "drizzle" : "drizzle";
215
+ if (!isDrizzleProject) {
216
+ throw new Error(
217
+ "Could not detect a drizzle project from this directory. Run inside a Drizzle project and pass --dialect/--adapter explicitly if needed."
218
+ );
219
+ }
220
+ if (inference.provider && inference.confidence >= 0.75) {
221
+ return {
222
+ adapter,
223
+ provider: inference.provider,
224
+ confidence: inference.confidence,
225
+ evidence,
226
+ prompted: false
227
+ };
228
+ }
229
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
230
+ throw new Error(
231
+ "Could not infer database provider confidently. Re-run with --dialect <sqlite|postgresql|pg>."
232
+ );
233
+ }
234
+ const prompt = await promptWithChoices(
235
+ `Could not infer the database provider from repo signals.
236
+ Select provider [sqlite|postgresql|mysql] (default sqlite): `,
237
+ ["sqlite", "postgresql", "mysql"],
238
+ "sqlite"
239
+ );
240
+ return {
241
+ adapter,
242
+ provider: prompt,
243
+ confidence: inference.confidence,
244
+ evidence,
245
+ prompted: true
246
+ };
247
+ }
248
+
249
+ // src/cli/generate.ts
250
+ function usage() {
251
+ return [
252
+ "Usage: sandkit generate [--adapter drizzle] [--dialect <sqlite|postgresql|pg>] [--out <file>] [--stdout]",
253
+ "Example: npx @giselles-ai/sandkit generate --dialect sqlite --stdout",
254
+ "Example: npx @giselles-ai/sandkit generate --adapter drizzle --dialect postgresql --stdout",
255
+ "If --stdout is omitted, output is shown to console by default.",
256
+ "Default output file: db/schema/sandkit.ts"
257
+ ].join("\n");
258
+ }
259
+ function parseGenerateArgs(argv) {
260
+ let provider;
261
+ let dialect;
262
+ let adapter;
263
+ let out;
264
+ let stdout = false;
265
+ for (let i = 0; i < argv.length; i++) {
266
+ const arg = argv[i];
267
+ if (arg === "--adapter") {
268
+ const next = argv[i + 1];
269
+ if (next === "drizzle") {
270
+ adapter = next;
271
+ i++;
272
+ continue;
273
+ }
274
+ throw new Error("Unsupported --adapter value. Use --adapter drizzle.");
275
+ }
276
+ if (arg === "--provider") {
277
+ const next = argv[i + 1];
278
+ if (next === "sqlite" || next === "postgresql" || next === "mysql") {
279
+ provider = next;
280
+ i++;
281
+ continue;
282
+ }
283
+ throw new Error(`Invalid provider: ${next}`);
284
+ }
285
+ if (arg === "--dialect") {
286
+ const next = argv[i + 1];
287
+ if (next === "sqlite" || next === "postgresql" || next === "pg") {
288
+ dialect = next === "pg" ? "postgresql" : next;
289
+ i++;
290
+ continue;
291
+ }
292
+ throw new Error(`Invalid dialect: ${next}`);
293
+ }
294
+ if (arg === "--out") {
295
+ const next = argv[i + 1];
296
+ if (!next) {
297
+ throw new Error("Missing --out path");
298
+ }
299
+ out = next;
300
+ i++;
301
+ continue;
302
+ }
303
+ if (arg === "--stdout") {
304
+ stdout = true;
305
+ continue;
306
+ }
307
+ if (arg === "--help" || arg === "-h") {
308
+ throw new Error(usage());
309
+ }
310
+ if (arg === "generate") {
311
+ continue;
312
+ }
313
+ throw new Error(`Unknown option: ${arg}`);
314
+ }
315
+ return {
316
+ provider,
317
+ dialect,
318
+ adapter,
319
+ out,
320
+ stdout
321
+ };
322
+ }
323
+ function runGenerateCommand(options) {
324
+ const snapshot = createModelSnapshot(options.provider);
325
+ const payload = createGeneratePayload(options.provider, snapshot.model);
326
+ const defaultOutputFile = options.adapter === "drizzle" || options.adapter === void 0 ? "db/schema/sandkit.ts" : `sandkit-schema.${options.provider}.ts`;
327
+ const outputFile = options.out ?? defaultOutputFile;
328
+ const result = {
329
+ command: "generate",
330
+ provider: options.provider,
331
+ outputTarget: options.stdout ? "stdout" : "file",
332
+ outputFile: options.stdout ? null : outputFile,
333
+ payload: {
334
+ generatedAt: payload.generatedAt,
335
+ summary: payload.summary,
336
+ schemaText: payload.schemaText
337
+ }
338
+ };
339
+ if (!options.stdout) {
340
+ mkdirSync(dirname(outputFile), { recursive: true });
341
+ writeFileSync(outputFile, payload.schemaText, "utf8");
342
+ }
343
+ return result;
344
+ }
345
+ async function runCliGenerate(argv) {
346
+ const options = parseGenerateArgs(argv);
347
+ const discovered = await resolveProviderWithDiscovery({
348
+ provider: options.provider,
349
+ adapter: options.adapter,
350
+ dialect: options.dialect,
351
+ stdout: false,
352
+ out: options.out
353
+ });
354
+ return runGenerateCommand({
355
+ provider: discovered.provider,
356
+ adapter: discovered.adapter,
357
+ dialect: options.dialect,
358
+ out: options.out,
359
+ stdout: options.stdout
360
+ });
361
+ }
362
+
363
+ // src/cli/index.ts
364
+ function buildHelp() {
365
+ return [
366
+ "Sandkit CLI",
367
+ "Commands: generate",
368
+ "",
369
+ " sandkit generate [--adapter drizzle] [--dialect <sqlite|postgresql|pg>] [--stdout] [--out file]",
370
+ "",
371
+ "When --stdout is set, output is returned in memory and printed by caller."
372
+ ].join("\n");
373
+ }
374
+ function parseArgs(argv) {
375
+ if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
376
+ throw new Error(buildHelp());
377
+ }
378
+ const command = argv[0];
379
+ if (command !== "generate") {
380
+ throw new Error(`Unknown command: ${command}`);
381
+ }
382
+ const rest = argv.slice(1);
383
+ const commandOptions = parseGenerateArgs(rest);
384
+ return { command, ...commandOptions, rest };
385
+ }
386
+ async function runCli(argv = process.argv.slice(2)) {
387
+ const parsed = parseArgs(argv);
388
+ if (parsed.command === "generate") {
389
+ const { rest } = parsed;
390
+ return runCliGenerate(rest);
391
+ }
392
+ throw new Error(`Unhandled command: ${parsed.command}`);
393
+ }
394
+ export {
395
+ runCli
396
+ };
397
+ //# sourceMappingURL=index.js.map