Skip to content

Commit ebe25a6

Browse files
authored
Merge pull request #298 from cloudflare/nightly
Release: Nightly -> Main
2 parents 03399f4 + 9571411 commit ebe25a6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+4422
-674
lines changed

sdk/README.md

Lines changed: 384 additions & 73 deletions
Large diffs are not rendered by default.

sdk/bun.lock

Lines changed: 364 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/package.json

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,37 @@
11
{
22
"name": "@cf-vibesdk/sdk",
3-
"version": "0.0.3",
3+
"version": "0.0.9",
44
"type": "module",
55
"exports": {
66
".": {
77
"types": "./dist/index.d.ts",
88
"default": "./dist/index.js"
9-
},
10-
"./node": {
11-
"types": "./dist/node.d.ts",
12-
"default": "./dist/node.js"
139
}
1410
},
1511
"files": [
1612
"dist"
1713
],
14+
"engines": {
15+
"node": ">=22"
16+
},
1817
"publishConfig": {
1918
"access": "public"
2019
},
2120
"scripts": {
22-
"build": "rm -rf ./dist && bun build ./src/index.ts ./src/node.ts --outdir ./dist --target bun",
23-
"bundle-types": "dts-bundle-generator --export-referenced-types false --project ./tsconfig.protocol.json -o ./dist/index.d.ts ./src/index.ts && dts-bundle-generator --export-referenced-types false --project ./tsconfig.protocol.json -o ./dist/node.d.ts ./src/node.ts",
21+
"build": "rm -rf ./dist && bun build ./src/index.ts --outdir ./dist --target browser",
22+
"bundle-types": "dts-bundle-generator --export-referenced-types false --no-check --project ./tsconfig.protocol.json -o ./dist/index.d.ts ./src/index.ts && bun run scripts/expand-drizzle-types.ts",
2423
"typecheck": "tsc -p ./tsconfig.json --noEmit",
2524
"test": "bun test test/*.test.ts",
26-
"test:integration": "bun test --timeout 600000 test/integration/*.test.ts"
27-
},
28-
"dependencies": {
29-
"ws": "^8.18.3"
25+
"test:integration": "bun test --timeout 600000 test/integration/*.test.ts",
26+
"package": "bun run typecheck && bun run test && bun run build && bun run bundle-types"
3027
},
3128
"devDependencies": {
32-
"@types/ws": "^8.18.1",
29+
"@cloudflare/workers-types": "^4.20241218.0",
30+
"@types/node": "^25.0.3",
3331
"dts-bundle-generator": "^9.5.1",
34-
"typescript": "^5.9.3"
32+
"miniflare": "^4.20251217.0",
33+
"puppeteer": "^24.8.0",
34+
"typescript": "^5.9.3",
35+
"wrangler": "^4.14.1"
3536
}
3637
}
37-
38-
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/**
2+
* Post-processes bundled .d.ts to expand Drizzle $inferSelect types.
3+
*/
4+
5+
import { readFileSync, writeFileSync } from 'fs';
6+
7+
const DTS_PATH = './dist/index.d.ts';
8+
9+
interface ColumnInfo {
10+
name: string;
11+
dataType: string;
12+
notNull: boolean;
13+
}
14+
15+
interface TableInfo {
16+
columns: Record<string, ColumnInfo>;
17+
}
18+
19+
// Store expanded types for later reference
20+
const expandedTypes = new Map<string, TableInfo>();
21+
22+
/**
23+
* Parse a table definition from the .d.ts content to extract column info
24+
*/
25+
function parseTableDefinition(content: string, tableName: string): TableInfo | null {
26+
// Match: declare const tableName: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
27+
const tableRegex = new RegExp(
28+
`declare const ${tableName}:\\s*import\\("drizzle-orm/sqlite-core"\\)\\.SQLiteTableWithColumns<\\{[^}]*columns:\\s*\\{`,
29+
's'
30+
);
31+
32+
const tableMatch = content.match(tableRegex);
33+
if (!tableMatch) {
34+
return null;
35+
}
36+
37+
const startIndex = tableMatch.index! + tableMatch[0].length;
38+
39+
// Find the columns block by tracking brace depth
40+
let braceDepth = 1;
41+
let endIndex = startIndex;
42+
for (let i = startIndex; i < content.length && braceDepth > 0; i++) {
43+
if (content[i] === '{') braceDepth++;
44+
if (content[i] === '}') braceDepth--;
45+
endIndex = i;
46+
}
47+
48+
const columnsBlock = content.slice(startIndex, endIndex);
49+
50+
// Parse each column
51+
const columns: Record<string, ColumnInfo> = {};
52+
53+
// Match column definitions: columnName: import("drizzle-orm/sqlite-core").SQLiteColumn<{...}>
54+
const columnRegex = /(\w+):\s*import\("drizzle-orm\/sqlite-core"\)\.SQLiteColumn<\{([^>]+)\}>/gs;
55+
let match;
56+
57+
while ((match = columnRegex.exec(columnsBlock)) !== null) {
58+
const columnName = match[1];
59+
const columnDef = match[2];
60+
61+
// Extract data type
62+
const dataMatch = columnDef.match(/data:\s*([^;]+);/);
63+
// Extract notNull
64+
const notNullMatch = columnDef.match(/notNull:\s*(true|false)/);
65+
66+
if (dataMatch) {
67+
const dataType = dataMatch[1].trim();
68+
69+
columns[columnName] = {
70+
name: columnName,
71+
dataType,
72+
notNull: notNullMatch ? notNullMatch[1] === 'true' : false,
73+
};
74+
}
75+
}
76+
77+
return { columns };
78+
}
79+
80+
/**
81+
* Generate an expanded type string from column info
82+
* @param serializeDates - if true, convert Date to string (for Serialized<T> types)
83+
*/
84+
function generateExpandedType(tableInfo: TableInfo, serializeDates = false): string {
85+
const fields = Object.entries(tableInfo.columns)
86+
.map(([name, col]) => {
87+
let dataType = col.dataType;
88+
89+
// Convert Date to string for serialized types
90+
if (serializeDates && dataType === 'Date') {
91+
dataType = 'string';
92+
}
93+
94+
const type = col.notNull ? dataType : `${dataType} | null`;
95+
return `\t${name}: ${type};`;
96+
})
97+
.join('\n');
98+
99+
return `{\n${fields}\n}`;
100+
}
101+
102+
/**
103+
* Find all tables referenced by $inferSelect in the content
104+
*/
105+
function findInferSelectReferences(content: string): Map<string, string[]> {
106+
const refs = new Map<string, string[]>();
107+
108+
// Match: type TypeName = typeof tableName.$inferSelect;
109+
const regex = /type\s+(\w+)\s*=\s*typeof\s+(\w+)\.\$inferSelect;/g;
110+
let match;
111+
112+
while ((match = regex.exec(content)) !== null) {
113+
const typeName = match[1];
114+
const tableName = match[2];
115+
116+
if (!refs.has(tableName)) {
117+
refs.set(tableName, []);
118+
}
119+
refs.get(tableName)!.push(typeName);
120+
}
121+
122+
return refs;
123+
}
124+
125+
/**
126+
* Find Serialized<TypeName> patterns where TypeName is an expanded type
127+
*/
128+
function expandSerializedTypes(content: string): string {
129+
// Find patterns like: type Foo = Serialized<Bar>;
130+
// where Bar is one of our expanded types
131+
132+
for (const [typeName, tableInfo] of expandedTypes) {
133+
// Match: Serialized<TypeName>
134+
const serializedRegex = new RegExp(`Serialized<${typeName}>`, 'g');
135+
136+
if (serializedRegex.test(content)) {
137+
const serializedExpanded = generateExpandedType(tableInfo, true);
138+
content = content.replace(serializedRegex, serializedExpanded);
139+
console.log(` Expanded Serialized<${typeName}>`);
140+
}
141+
}
142+
143+
return content;
144+
}
145+
146+
function main() {
147+
console.log('Expanding Drizzle $inferSelect types...');
148+
149+
let content = readFileSync(DTS_PATH, 'utf-8');
150+
151+
// Find all $inferSelect references
152+
const inferSelectRefs = findInferSelectReferences(content);
153+
console.log(`Found ${inferSelectRefs.size} tables with $inferSelect references`);
154+
155+
// Process each table
156+
for (const [tableName, typeNames] of inferSelectRefs) {
157+
console.log(` Processing table: ${tableName} (types: ${typeNames.join(', ')})`);
158+
159+
const tableInfo = parseTableDefinition(content, tableName);
160+
if (!tableInfo) {
161+
console.warn(` Warning: Could not parse table definition for ${tableName}`);
162+
continue;
163+
}
164+
165+
const columnCount = Object.keys(tableInfo.columns).length;
166+
console.log(` Found ${columnCount} columns`);
167+
168+
const expandedType = generateExpandedType(tableInfo);
169+
170+
// Replace each type alias with the expanded type
171+
for (const typeName of typeNames) {
172+
const oldDecl = `type ${typeName} = typeof ${tableName}.$inferSelect;`;
173+
const newDecl = `type ${typeName} = ${expandedType}`;
174+
175+
if (content.includes(oldDecl)) {
176+
content = content.replace(oldDecl, newDecl);
177+
console.log(` Replaced: ${typeName}`);
178+
179+
// Store for Serialized expansion
180+
expandedTypes.set(typeName, tableInfo);
181+
}
182+
}
183+
}
184+
185+
// Also remove $inferInsert references (replace with unknown for now)
186+
const inferInsertRegex = /type\s+(\w+)\s*=\s*typeof\s+(\w+)\.\$inferInsert;/g;
187+
content = content.replace(inferInsertRegex, (match, typeName, tableName) => {
188+
console.log(` Removing $inferInsert reference: ${typeName}`);
189+
return `type ${typeName} = Record<string, unknown>;`;
190+
});
191+
192+
// Now expand Serialized<T> types
193+
console.log('\nExpanding Serialized<T> types...');
194+
content = expandSerializedTypes(content);
195+
196+
writeFileSync(DTS_PATH, content);
197+
console.log('\nDone!');
198+
}
199+
200+
main();

sdk/src/blueprint.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
/**
2-
* Utility for checking if a value is a plain object.
3-
*/
4-
export function isRecord(v: unknown): v is Record<string, unknown> {
5-
return Boolean(v) && typeof v === 'object' && !Array.isArray(v);
6-
}
1+
import { isRecord } from './utils';
72

83
/**
94
* Blueprint structure as streamed from the agent.

0 commit comments

Comments
 (0)