Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b9f0416
feat: refactor client methods to include app management functionalities
AshishKumar4 Dec 24, 2025
47eba5f
feat: enhance type exports and improve utility functions for better t…
AshishKumar4 Dec 24, 2025
b7be5db
feat: wrap global fetch to preserve context in Workers runtime
AshishKumar4 Dec 24, 2025
1fe8640
feat: implement WebSocket ticket authentication and management system
AshishKumar4 Dec 25, 2025
eb45e2c
Merge pull request #294 from cloudflare/feat/ws-ticketing-auth
AshishKumar4 Dec 25, 2025
932271f
feat: sdk ws ticket auth and auto reconnect
AshishKumar4 Dec 25, 2025
a841168
feat: sdk tests
AshishKumar4 Dec 25, 2025
33f72ab
feat: readme update
AshishKumar4 Dec 25, 2025
71cab7b
Update sdk/src/types.ts
AshishKumar4 Dec 27, 2025
dc48247
Update sdk/src/types.ts
AshishKumar4 Dec 27, 2025
f95b1c8
Merge pull request #293 from cloudflare/fix/sdk-fixes
AshishKumar4 Dec 27, 2025
0bc7db9
feat: use original worker types in sdk
AshishKumar4 Dec 27, 2025
6745f04
Merge pull request #296 from cloudflare/fix/sdk-fixes
AshishKumar4 Dec 27, 2025
33eb39f
feat: expand drizzle types script on sdk bundle
AshishKumar4 Dec 27, 2025
96b709e
Merge pull request #297 from cloudflare/fix/sdk-fixes
AshishKumar4 Dec 28, 2025
a68d9cb
feat: integration tests with worker
AshishKumar4 Dec 28, 2025
f565aff
Merge pull request #299 from cloudflare/fix/sdk-fixes
AshishKumar4 Dec 28, 2025
dbcf51f
feat: implement phase timeline management and state tracking in SDK
AshishKumar4 Dec 29, 2025
969bea1
Merge pull request #300 from cloudflare/feat/sdk-phase-timeline
AshishKumar4 Dec 29, 2025
f8b14cd
feat: add phase timeline change subscription and event handling
AshishKumar4 Dec 29, 2025
a1c7404
Merge pull request #301 from cloudflare/feat/sdk-phase-timeline
AshishKumar4 Dec 29, 2025
3c4f203
feat: add app visibility check for preview deployment
AshishKumar4 Jan 9, 2026
9571411
Merge pull request #306 from AshishKumar4/fix/public-check-on-deploy-…
karishnu Jan 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
457 changes: 384 additions & 73 deletions sdk/README.md

Large diffs are not rendered by default.

371 changes: 364 additions & 7 deletions sdk/bun.lock

Large diffs are not rendered by default.

29 changes: 14 additions & 15 deletions sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
{
"name": "@cf-vibesdk/sdk",
"version": "0.0.3",
"version": "0.0.9",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./node": {
"types": "./dist/node.d.ts",
"default": "./dist/node.js"
}
},
"files": [
"dist"
],
"engines": {
"node": ">=22"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "rm -rf ./dist && bun build ./src/index.ts ./src/node.ts --outdir ./dist --target bun",
"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",
"build": "rm -rf ./dist && bun build ./src/index.ts --outdir ./dist --target browser",
"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",
"typecheck": "tsc -p ./tsconfig.json --noEmit",
"test": "bun test test/*.test.ts",
"test:integration": "bun test --timeout 600000 test/integration/*.test.ts"
},
"dependencies": {
"ws": "^8.18.3"
"test:integration": "bun test --timeout 600000 test/integration/*.test.ts",
"package": "bun run typecheck && bun run test && bun run build && bun run bundle-types"
},
"devDependencies": {
"@types/ws": "^8.18.1",
"@cloudflare/workers-types": "^4.20241218.0",
"@types/node": "^25.0.3",
"dts-bundle-generator": "^9.5.1",
"typescript": "^5.9.3"
"miniflare": "^4.20251217.0",
"puppeteer": "^24.8.0",
"typescript": "^5.9.3",
"wrangler": "^4.14.1"
}
}


200 changes: 200 additions & 0 deletions sdk/scripts/expand-drizzle-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/**
* Post-processes bundled .d.ts to expand Drizzle $inferSelect types.
*/

import { readFileSync, writeFileSync } from 'fs';

const DTS_PATH = './dist/index.d.ts';

interface ColumnInfo {
name: string;
dataType: string;
notNull: boolean;
}

interface TableInfo {
columns: Record<string, ColumnInfo>;
}

// Store expanded types for later reference
const expandedTypes = new Map<string, TableInfo>();

/**
* Parse a table definition from the .d.ts content to extract column info
*/
function parseTableDefinition(content: string, tableName: string): TableInfo | null {
// Match: declare const tableName: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
const tableRegex = new RegExp(
`declare const ${tableName}:\\s*import\\("drizzle-orm/sqlite-core"\\)\\.SQLiteTableWithColumns<\\{[^}]*columns:\\s*\\{`,
's'
);

const tableMatch = content.match(tableRegex);
if (!tableMatch) {
return null;
}

const startIndex = tableMatch.index! + tableMatch[0].length;

// Find the columns block by tracking brace depth
let braceDepth = 1;
let endIndex = startIndex;
for (let i = startIndex; i < content.length && braceDepth > 0; i++) {
if (content[i] === '{') braceDepth++;
if (content[i] === '}') braceDepth--;
endIndex = i;
}

const columnsBlock = content.slice(startIndex, endIndex);

// Parse each column
const columns: Record<string, ColumnInfo> = {};

// Match column definitions: columnName: import("drizzle-orm/sqlite-core").SQLiteColumn<{...}>
const columnRegex = /(\w+):\s*import\("drizzle-orm\/sqlite-core"\)\.SQLiteColumn<\{([^>]+)\}>/gs;
let match;

while ((match = columnRegex.exec(columnsBlock)) !== null) {
const columnName = match[1];
const columnDef = match[2];

// Extract data type
const dataMatch = columnDef.match(/data:\s*([^;]+);/);
// Extract notNull
const notNullMatch = columnDef.match(/notNull:\s*(true|false)/);

if (dataMatch) {
const dataType = dataMatch[1].trim();

columns[columnName] = {
name: columnName,
dataType,
notNull: notNullMatch ? notNullMatch[1] === 'true' : false,
};
}
}

return { columns };
}

/**
* Generate an expanded type string from column info
* @param serializeDates - if true, convert Date to string (for Serialized<T> types)
*/
function generateExpandedType(tableInfo: TableInfo, serializeDates = false): string {
const fields = Object.entries(tableInfo.columns)
.map(([name, col]) => {
let dataType = col.dataType;

// Convert Date to string for serialized types
if (serializeDates && dataType === 'Date') {
dataType = 'string';
}

const type = col.notNull ? dataType : `${dataType} | null`;
return `\t${name}: ${type};`;
})
.join('\n');

return `{\n${fields}\n}`;
}

/**
* Find all tables referenced by $inferSelect in the content
*/
function findInferSelectReferences(content: string): Map<string, string[]> {
const refs = new Map<string, string[]>();

// Match: type TypeName = typeof tableName.$inferSelect;
const regex = /type\s+(\w+)\s*=\s*typeof\s+(\w+)\.\$inferSelect;/g;
let match;

while ((match = regex.exec(content)) !== null) {
const typeName = match[1];
const tableName = match[2];

if (!refs.has(tableName)) {
refs.set(tableName, []);
}
refs.get(tableName)!.push(typeName);
}

return refs;
}

/**
* Find Serialized<TypeName> patterns where TypeName is an expanded type
*/
function expandSerializedTypes(content: string): string {
// Find patterns like: type Foo = Serialized<Bar>;
// where Bar is one of our expanded types

for (const [typeName, tableInfo] of expandedTypes) {
// Match: Serialized<TypeName>
const serializedRegex = new RegExp(`Serialized<${typeName}>`, 'g');

if (serializedRegex.test(content)) {
const serializedExpanded = generateExpandedType(tableInfo, true);
content = content.replace(serializedRegex, serializedExpanded);
console.log(` Expanded Serialized<${typeName}>`);
}
}

return content;
}

function main() {
console.log('Expanding Drizzle $inferSelect types...');

let content = readFileSync(DTS_PATH, 'utf-8');

// Find all $inferSelect references
const inferSelectRefs = findInferSelectReferences(content);
console.log(`Found ${inferSelectRefs.size} tables with $inferSelect references`);

// Process each table
for (const [tableName, typeNames] of inferSelectRefs) {
console.log(` Processing table: ${tableName} (types: ${typeNames.join(', ')})`);

const tableInfo = parseTableDefinition(content, tableName);
if (!tableInfo) {
console.warn(` Warning: Could not parse table definition for ${tableName}`);
continue;
}

const columnCount = Object.keys(tableInfo.columns).length;
console.log(` Found ${columnCount} columns`);

const expandedType = generateExpandedType(tableInfo);

// Replace each type alias with the expanded type
for (const typeName of typeNames) {
const oldDecl = `type ${typeName} = typeof ${tableName}.$inferSelect;`;
const newDecl = `type ${typeName} = ${expandedType}`;

if (content.includes(oldDecl)) {
content = content.replace(oldDecl, newDecl);
console.log(` Replaced: ${typeName}`);

// Store for Serialized expansion
expandedTypes.set(typeName, tableInfo);
}
}
}

// Also remove $inferInsert references (replace with unknown for now)
const inferInsertRegex = /type\s+(\w+)\s*=\s*typeof\s+(\w+)\.\$inferInsert;/g;
content = content.replace(inferInsertRegex, (match, typeName, tableName) => {
console.log(` Removing $inferInsert reference: ${typeName}`);
return `type ${typeName} = Record<string, unknown>;`;
});

// Now expand Serialized<T> types
console.log('\nExpanding Serialized<T> types...');
content = expandSerializedTypes(content);

writeFileSync(DTS_PATH, content);
console.log('\nDone!');
}

main();
7 changes: 1 addition & 6 deletions sdk/src/blueprint.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
/**
* Utility for checking if a value is a plain object.
*/
export function isRecord(v: unknown): v is Record<string, unknown> {
return Boolean(v) && typeof v === 'object' && !Array.isArray(v);
}
import { isRecord } from './utils';

/**
* Blueprint structure as streamed from the agent.
Expand Down
Loading
Loading