@cf-vibesdk/sdk 0.0.7 → 0.0.9

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/README.md CHANGED
@@ -96,13 +96,21 @@ const session = await client.build('Build a weather dashboard', {
96
96
 
97
97
  ### `client.connect(agentId)`
98
98
 
99
- Connect to an existing app session.
99
+ Connect to an existing app session. State is automatically restored from the agent, including:
100
+ - Phase timeline with completion status
101
+ - Generated files
102
+ - Agent metadata (query, projectName, behaviorType, etc.)
100
103
 
101
104
  ```ts
102
105
  const session = await client.connect('agent-id-here', {
103
106
  credentials: { ... }, // Optional
104
107
  });
105
108
  await session.connect();
109
+
110
+ // State is now seeded from the agent
111
+ console.log('Original query:', session.state.get().query);
112
+ console.log('Phases:', session.phases.list());
113
+ console.log('Files:', session.files.listPaths());
106
114
  ```
107
115
 
108
116
  ## App Management
@@ -215,6 +223,71 @@ const snapshot = session.files.snapshot(); // { 'src/App.tsx': '...', ... }
215
223
  const tree = session.files.tree(); // Nested file tree structure
216
224
  ```
217
225
 
226
+ ### Phase Timeline
227
+
228
+ Access the full phase timeline for phasic builds. The timeline is automatically seeded when connecting to an existing agent and updated as phases progress.
229
+
230
+ ```ts
231
+ // Get all phases
232
+ const phases = session.phases.list();
233
+ // [{ id: 'phase-0', name: 'Core Setup', status: 'completed', files: [...] }, ...]
234
+
235
+ // Get current active phase
236
+ const current = session.phases.current();
237
+ if (current) {
238
+ console.log(`Working on: ${current.name}`);
239
+ console.log(`Status: ${current.status}`); // 'generating' | 'implementing' | 'validating'
240
+ }
241
+
242
+ // Get completed phases
243
+ const done = session.phases.completed();
244
+ console.log(`Progress: ${done.length}/${session.phases.count()}`);
245
+
246
+ // Check if all phases are done
247
+ if (session.phases.allCompleted()) {
248
+ console.log('Build complete!');
249
+ }
250
+
251
+ // Get phase by ID
252
+ const phase = session.phases.get('phase-0');
253
+
254
+ // Subscribe to phase changes
255
+ const unsubscribe = session.phases.onChange((event) => {
256
+ console.log(`Phase ${event.type}:`, event.phase.name);
257
+ console.log(`Status: ${event.phase.status}`);
258
+ console.log(`Total phases: ${event.allPhases.length}`);
259
+ });
260
+ // Later: unsubscribe();
261
+ ```
262
+
263
+ The `onChange` callback receives a `PhaseTimelineEvent`:
264
+
265
+ ```ts
266
+ type PhaseTimelineEvent = {
267
+ type: 'added' | 'updated'; // New phase vs status/file change
268
+ phase: PhaseInfo; // The affected phase
269
+ allPhases: PhaseInfo[]; // All phases after this change
270
+ };
271
+ ```
272
+
273
+ Each phase contains:
274
+
275
+ ```ts
276
+ type PhaseInfo = {
277
+ id: string; // 'phase-0', 'phase-1', etc.
278
+ name: string; // 'Core Setup', 'Authentication', etc.
279
+ description: string; // What the phase accomplishes
280
+ status: PhaseStatus; // 'pending' | 'generating' | 'implementing' | 'validating' | 'completed' | 'cancelled'
281
+ files: PhaseFile[]; // Files in this phase
282
+ };
283
+
284
+ type PhaseFile = {
285
+ path: string; // 'src/App.tsx'
286
+ purpose: string; // 'Main application component'
287
+ status: PhaseFileStatus; // 'pending' | 'generating' | 'completed' | 'cancelled'
288
+ };
289
+ ```
290
+
218
291
  ### State
219
292
 
220
293
  ```ts
@@ -226,6 +299,16 @@ console.log(state.phase); // { status: 'idle' | 'generating' | ... }
226
299
  console.log(state.preview); // Preview deployment state
227
300
  console.log(state.cloudflare); // Cloudflare deployment state
228
301
 
302
+ // Phase timeline (array of all phases)
303
+ console.log(state.phases); // [{ id, name, status, files }, ...]
304
+
305
+ // Agent metadata (seeded from agent_connected)
306
+ console.log(state.behaviorType); // 'phasic' | 'agentic'
307
+ console.log(state.projectType); // 'app' | 'workflow' | etc.
308
+ console.log(state.query); // Original user prompt
309
+ console.log(state.projectName); // Project name from blueprint
310
+ console.log(state.shouldBeGenerating); // Whether agent is actively generating
311
+
229
312
  // Subscribe to changes
230
313
  session.state.onChange((next, prev) => {
231
314
  console.log('State changed:', next);
@@ -324,10 +407,24 @@ All types are exported:
324
407
 
325
408
  ```ts
326
409
  import type {
410
+ // Client & Session
327
411
  VibeClientOptions,
328
412
  BuildOptions,
329
413
  BuildSession,
330
414
  SessionState,
415
+ SessionFiles,
416
+ SessionPhases,
417
+
418
+ // Phase Timeline
419
+ PhaseInfo,
420
+ PhaseFile,
421
+ PhaseStatus,
422
+ PhaseFileStatus,
423
+ PhaseEventType,
424
+ PhaseTimelineEvent,
425
+ PhaseTimelineChangeType,
426
+
427
+ // API
331
428
  ApiResponse,
332
429
  AppDetails,
333
430
  Credentials,
package/dist/index.d.ts CHANGED
@@ -5568,6 +5568,8 @@ export type AgentEventMap = {
5568
5568
  error: {
5569
5569
  error: string;
5570
5570
  };
5571
+ /** Emitted when the phase timeline changes (phase added or updated). */
5572
+ phases: PhaseTimelineEvent;
5571
5573
  };
5572
5574
  /**
5573
5575
  * URL provider for WebSocket connections.
@@ -5613,6 +5615,62 @@ export type PhaseEventType = "phase_generating" | "phase_generated" | "phase_imp
5613
5615
  export type WaitForPhaseOptions = WaitOptions & {
5614
5616
  type: PhaseEventType;
5615
5617
  };
5618
+ /**
5619
+ * Status of a file within a phase.
5620
+ */
5621
+ export type PhaseFileStatus = "pending" | "generating" | "completed" | "cancelled";
5622
+ /**
5623
+ * A file concept within a phase, with its generation status.
5624
+ * Extends platform's FileConceptType with status tracking.
5625
+ */
5626
+ export type PhaseFile = Pick<FileConceptType, "path" | "purpose"> & {
5627
+ status: PhaseFileStatus;
5628
+ };
5629
+ /**
5630
+ * Status of a phase in the timeline.
5631
+ */
5632
+ export type PhaseStatus = "pending" | "generating" | "implementing" | "validating" | "completed" | "cancelled";
5633
+ /**
5634
+ * A phase in the build timeline with its files and status.
5635
+ * Extends platform's PhaseConceptType with SDK-specific fields.
5636
+ */
5637
+ export type PhaseInfo = Pick<PhaseConceptType, "name" | "description"> & {
5638
+ /** Unique identifier for this phase (e.g., "phase-0", "phase-1"). */
5639
+ id: string;
5640
+ /** Current status of the phase. */
5641
+ status: PhaseStatus;
5642
+ /** Files in this phase with their generation status. */
5643
+ files: PhaseFile[];
5644
+ };
5645
+ /**
5646
+ * High-level API for accessing phase timeline data.
5647
+ */
5648
+ export type SessionPhases = {
5649
+ /** Get all phases in the timeline. */
5650
+ list: () => PhaseInfo[];
5651
+ /** Get the currently active phase (first non-completed phase), or undefined. */
5652
+ current: () => PhaseInfo | undefined;
5653
+ /** Get all completed phases. */
5654
+ completed: () => PhaseInfo[];
5655
+ /** Get a phase by its id (e.g., "phase-0"). */
5656
+ get: (id: string) => PhaseInfo | undefined;
5657
+ /** Get the total count of phases. */
5658
+ count: () => number;
5659
+ /** Check if all phases are completed. */
5660
+ allCompleted: () => boolean;
5661
+ };
5662
+ /**
5663
+ * Event emitted when the phase timeline changes.
5664
+ */
5665
+ export type PhaseTimelineChangeType = "added" | "updated";
5666
+ export type PhaseTimelineEvent = {
5667
+ /** Type of change: 'added' for new phase, 'updated' for status/file changes. */
5668
+ type: PhaseTimelineChangeType;
5669
+ /** The phase that was added or updated. */
5670
+ phase: PhaseInfo;
5671
+ /** All phases in the timeline after this change. */
5672
+ allPhases: PhaseInfo[];
5673
+ };
5616
5674
  export type SessionDeployable = {
5617
5675
  files: number;
5618
5676
  reason: "generation_complete" | "phase_validated";
@@ -5697,15 +5755,48 @@ export type SessionState = {
5697
5755
  preview: PreviewDeploymentState;
5698
5756
  cloudflare: CloudflareDeploymentState;
5699
5757
  lastError?: string;
5758
+ /** Full phase timeline with status and files for each phase. */
5759
+ phases: PhaseInfo[];
5760
+ /** Behavior type of the agent (phasic or agentic). */
5761
+ behaviorType?: BehaviorType$1;
5762
+ /** Project type (app, workflow, presentation, general). */
5763
+ projectType?: ProjectType$1;
5764
+ /** Original user query that started this build. */
5765
+ query?: string;
5766
+ /** Whether the agent should be actively generating. */
5767
+ shouldBeGenerating?: boolean;
5768
+ /** Project name from the blueprint. */
5769
+ projectName?: string;
5700
5770
  };
5701
5771
  export declare class SessionStateStore {
5702
5772
  private state;
5703
5773
  private emitter;
5704
5774
  get(): SessionState;
5705
5775
  onChange(cb: (next: SessionState, prev: SessionState) => void): () => void;
5776
+ /**
5777
+ * Subscribe to phase timeline changes.
5778
+ * Fires when a phase is added or when a phase's status/files change.
5779
+ */
5780
+ onPhaseChange(cb: (event: PhaseTimelineEvent) => void): () => void;
5706
5781
  setConnection(state: ConnectionState): void;
5707
5782
  applyWsMessage(msg: AgentWsServerMessage): void;
5783
+ /**
5784
+ * Update the status of a file in the phase timeline.
5785
+ */
5786
+ private updateFileStatus;
5787
+ /**
5788
+ * Update an existing phase or add a new one to the timeline.
5789
+ */
5790
+ private updateOrAddPhase;
5708
5791
  private setState;
5792
+ /**
5793
+ * Compare old and new phases arrays and emit change events.
5794
+ */
5795
+ private emitPhaseChanges;
5796
+ /**
5797
+ * Check if a phase has meaningfully changed (status or file statuses).
5798
+ */
5799
+ private hasPhaseChanged;
5709
5800
  clear(): void;
5710
5801
  }
5711
5802
  type WorkspaceChange = {
@@ -5775,6 +5866,30 @@ export declare class BuildSession {
5775
5866
  readonly workspace: WorkspaceStore;
5776
5867
  readonly state: SessionStateStore;
5777
5868
  readonly files: SessionFiles;
5869
+ /**
5870
+ * High-level API for accessing the phase timeline.
5871
+ * Phases are seeded from agent_connected and updated on phase events.
5872
+ */
5873
+ readonly phases: {
5874
+ /** Get all phases in the timeline. */
5875
+ list: () => PhaseInfo[];
5876
+ /** Get the currently active phase (first non-completed phase), or undefined. */
5877
+ current: () => PhaseInfo | undefined;
5878
+ /** Get all completed phases. */
5879
+ completed: () => PhaseInfo[];
5880
+ /** Get a phase by its id (e.g., "phase-0"). */
5881
+ get: (id: string) => PhaseInfo | undefined;
5882
+ /** Get the total count of phases. */
5883
+ count: () => number;
5884
+ /** Check if all phases are completed. */
5885
+ allCompleted: () => boolean;
5886
+ /**
5887
+ * Subscribe to phase timeline changes.
5888
+ * Fires when a phase is added or when a phase's status/files change.
5889
+ * @returns Unsubscribe function.
5890
+ */
5891
+ onChange: (cb: (event: PhaseTimelineEvent) => void) => (() => void);
5892
+ };
5778
5893
  readonly wait: {
5779
5894
  generationStarted: (options?: WaitOptions) => Promise<{
5780
5895
  type: "generation_started";
package/dist/index.js CHANGED
@@ -227,7 +227,8 @@ var INITIAL_STATE = {
227
227
  generation: { status: "idle" },
228
228
  phase: { status: "idle" },
229
229
  preview: { status: "idle" },
230
- cloudflare: { status: "idle" }
230
+ cloudflare: { status: "idle" },
231
+ phases: []
231
232
  };
232
233
  function extractPhaseInfo(msg) {
233
234
  const phase = msg?.phase;
@@ -236,6 +237,51 @@ function extractPhaseInfo(msg) {
236
237
  description: phase?.description
237
238
  };
238
239
  }
240
+ function extractPhaseFiles(msg) {
241
+ const phase = msg?.phase;
242
+ return phase?.files;
243
+ }
244
+ function isPhasicState(state) {
245
+ return state.behaviorType === "phasic" && "generatedPhases" in state;
246
+ }
247
+ function buildPhaseTimelineFromState(state, generatedFilesMap) {
248
+ if (!isPhasicState(state))
249
+ return [];
250
+ const isActivelyGenerating = state.shouldBeGenerating === true;
251
+ return state.generatedPhases.map((phase, index) => {
252
+ let status;
253
+ if (phase.completed) {
254
+ status = "completed";
255
+ } else if (!isActivelyGenerating) {
256
+ status = "cancelled";
257
+ } else {
258
+ status = "generating";
259
+ }
260
+ const files = phase.files.map((f) => {
261
+ const fileExists = f.path in generatedFilesMap;
262
+ let fileStatus;
263
+ if (fileExists) {
264
+ fileStatus = "completed";
265
+ } else if (!isActivelyGenerating) {
266
+ fileStatus = "cancelled";
267
+ } else {
268
+ fileStatus = "pending";
269
+ }
270
+ return {
271
+ path: f.path,
272
+ purpose: f.purpose,
273
+ status: fileStatus
274
+ };
275
+ });
276
+ return {
277
+ id: `phase-${index}`,
278
+ name: phase.name,
279
+ description: phase.description,
280
+ status,
281
+ files
282
+ };
283
+ });
284
+ }
239
285
 
240
286
  class SessionStateStore {
241
287
  state = INITIAL_STATE;
@@ -246,6 +292,9 @@ class SessionStateStore {
246
292
  onChange(cb) {
247
293
  return this.emitter.on("change", ({ prev, next }) => cb(next, prev));
248
294
  }
295
+ onPhaseChange(cb) {
296
+ return this.emitter.on("phaseChange", cb);
297
+ }
249
298
  setConnection(state) {
250
299
  this.setState({ connection: state });
251
300
  }
@@ -303,47 +352,90 @@ class SessionStateStore {
303
352
  }
304
353
  case "file_generating": {
305
354
  const m = msg;
306
- this.setState({ currentFile: m.filePath });
355
+ const phasesWithGenerating = this.updateFileStatus(m.filePath, "generating");
356
+ this.setState({ currentFile: m.filePath, phases: phasesWithGenerating });
307
357
  break;
308
358
  }
309
359
  case "file_generated": {
360
+ const m = msg;
361
+ const filePath = m.file?.filePath;
310
362
  const prev = this.state.generation;
363
+ const phasesWithCompleted = filePath ? this.updateFileStatus(filePath, "completed") : this.state.phases;
311
364
  if (prev.status === "running" || prev.status === "stopped") {
312
365
  this.setState({
313
366
  generation: { ...prev, filesGenerated: prev.filesGenerated + 1 },
314
- currentFile: undefined
367
+ currentFile: undefined,
368
+ phases: phasesWithCompleted
315
369
  });
370
+ } else {
371
+ this.setState({ phases: phasesWithCompleted, currentFile: undefined });
316
372
  }
317
373
  break;
318
374
  }
319
375
  case "phase_generating": {
320
376
  const m = msg;
321
- this.setState({ phase: { status: "generating", ...extractPhaseInfo(m) } });
377
+ const phaseInfo = extractPhaseInfo(m);
378
+ const phaseFiles = extractPhaseFiles(m);
379
+ const phases = this.updateOrAddPhase(phaseInfo, "generating", phaseFiles);
380
+ this.setState({
381
+ phase: { status: "generating", ...phaseInfo },
382
+ phases
383
+ });
322
384
  break;
323
385
  }
324
386
  case "phase_generated": {
325
387
  const m = msg;
326
- this.setState({ phase: { status: "generated", ...extractPhaseInfo(m) } });
388
+ const phaseInfo = extractPhaseInfo(m);
389
+ const phaseFiles = extractPhaseFiles(m);
390
+ const phases = this.updateOrAddPhase(phaseInfo, "implementing", phaseFiles);
391
+ this.setState({
392
+ phase: { status: "generated", ...phaseInfo },
393
+ phases
394
+ });
327
395
  break;
328
396
  }
329
397
  case "phase_implementing": {
330
398
  const m = msg;
331
- this.setState({ phase: { status: "implementing", ...extractPhaseInfo(m) } });
399
+ const phaseInfo = extractPhaseInfo(m);
400
+ const phaseFiles = extractPhaseFiles(m);
401
+ const phases = this.updateOrAddPhase(phaseInfo, "implementing", phaseFiles);
402
+ this.setState({
403
+ phase: { status: "implementing", ...phaseInfo },
404
+ phases
405
+ });
332
406
  break;
333
407
  }
334
408
  case "phase_implemented": {
335
409
  const m = msg;
336
- this.setState({ phase: { status: "implemented", ...extractPhaseInfo(m) } });
410
+ const phaseInfo = extractPhaseInfo(m);
411
+ const phaseFiles = extractPhaseFiles(m);
412
+ const phases = this.updateOrAddPhase(phaseInfo, "validating", phaseFiles);
413
+ this.setState({
414
+ phase: { status: "implemented", ...phaseInfo },
415
+ phases
416
+ });
337
417
  break;
338
418
  }
339
419
  case "phase_validating": {
340
420
  const m = msg;
341
- this.setState({ phase: { status: "validating", ...extractPhaseInfo(m) } });
421
+ const phaseInfo = extractPhaseInfo(m);
422
+ const phaseFiles = extractPhaseFiles(m);
423
+ const phases = this.updateOrAddPhase(phaseInfo, "validating", phaseFiles);
424
+ this.setState({
425
+ phase: { status: "validating", ...phaseInfo },
426
+ phases
427
+ });
342
428
  break;
343
429
  }
344
430
  case "phase_validated": {
345
431
  const m = msg;
346
- this.setState({ phase: { status: "validated", ...extractPhaseInfo(m) } });
432
+ const phaseInfo = extractPhaseInfo(m);
433
+ const phaseFiles = extractPhaseFiles(m);
434
+ const phases = this.updateOrAddPhase(phaseInfo, "completed", phaseFiles);
435
+ this.setState({
436
+ phase: { status: "validated", ...phaseInfo },
437
+ phases
438
+ });
347
439
  break;
348
440
  }
349
441
  case "deployment_started": {
@@ -394,9 +486,24 @@ class SessionStateStore {
394
486
  }
395
487
  case "agent_connected": {
396
488
  const m = msg;
489
+ const agentState = m.state;
397
490
  const previewUrl = m.previewUrl;
398
- if (previewUrl)
399
- this.setState({ previewUrl });
491
+ const phases = buildPhaseTimelineFromState(agentState, agentState.generatedFilesMap ?? {});
492
+ let generation = this.state.generation;
493
+ if (agentState.shouldBeGenerating) {
494
+ const filesGenerated = Object.keys(agentState.generatedFilesMap ?? {}).length;
495
+ generation = { status: "running", filesGenerated };
496
+ }
497
+ this.setState({
498
+ previewUrl,
499
+ phases,
500
+ generation,
501
+ behaviorType: agentState.behaviorType,
502
+ projectType: agentState.projectType,
503
+ query: agentState.query,
504
+ shouldBeGenerating: agentState.shouldBeGenerating,
505
+ projectName: agentState.projectName
506
+ });
400
507
  break;
401
508
  }
402
509
  case "error": {
@@ -408,11 +515,75 @@ class SessionStateStore {
408
515
  break;
409
516
  }
410
517
  }
518
+ updateFileStatus(filePath, status) {
519
+ return this.state.phases.map((phase) => ({
520
+ ...phase,
521
+ files: phase.files.map((f) => f.path === filePath ? { ...f, status } : f)
522
+ }));
523
+ }
524
+ updateOrAddPhase(phaseInfo, status, phaseFiles) {
525
+ const phases = [...this.state.phases];
526
+ const existingIndex = phases.findIndex((p) => p.name === phaseInfo.name);
527
+ const files = (phaseFiles ?? []).map((f) => ({
528
+ path: f.path,
529
+ purpose: f.purpose,
530
+ status: status === "completed" ? "completed" : "pending"
531
+ }));
532
+ if (existingIndex >= 0) {
533
+ phases[existingIndex] = {
534
+ ...phases[existingIndex],
535
+ status,
536
+ description: phaseInfo.description ?? phases[existingIndex].description,
537
+ files: files.length > 0 ? files : phases[existingIndex].files
538
+ };
539
+ } else if (phaseInfo.name) {
540
+ phases.push({
541
+ id: `phase-${phases.length}`,
542
+ name: phaseInfo.name,
543
+ description: phaseInfo.description ?? "",
544
+ status,
545
+ files
546
+ });
547
+ }
548
+ return phases;
549
+ }
411
550
  setState(patch) {
412
551
  const prev = this.state;
413
552
  const next = { ...prev, ...patch };
414
553
  this.state = next;
415
554
  this.emitter.emit("change", { prev, next });
555
+ if (patch.phases && patch.phases !== prev.phases) {
556
+ this.emitPhaseChanges(prev.phases, patch.phases);
557
+ }
558
+ }
559
+ emitPhaseChanges(prevPhases, nextPhases) {
560
+ for (const phase of nextPhases) {
561
+ const prevPhase = prevPhases.find((p) => p.id === phase.id);
562
+ if (!prevPhase) {
563
+ this.emitter.emit("phaseChange", {
564
+ type: "added",
565
+ phase,
566
+ allPhases: nextPhases
567
+ });
568
+ } else if (this.hasPhaseChanged(prevPhase, phase)) {
569
+ this.emitter.emit("phaseChange", {
570
+ type: "updated",
571
+ phase,
572
+ allPhases: nextPhases
573
+ });
574
+ }
575
+ }
576
+ }
577
+ hasPhaseChanged(prev, next) {
578
+ if (prev.status !== next.status)
579
+ return true;
580
+ if (prev.files.length !== next.files.length)
581
+ return true;
582
+ for (let i = 0;i < prev.files.length; i++) {
583
+ if (prev.files[i].status !== next.files[i].status)
584
+ return true;
585
+ }
586
+ return false;
416
587
  }
417
588
  clear() {
418
589
  this.state = INITIAL_STATE;
@@ -804,6 +975,15 @@ class BuildSession {
804
975
  snapshot: () => this.workspace.snapshot(),
805
976
  tree: () => buildFileTree(this.workspace.paths())
806
977
  };
978
+ phases = {
979
+ list: () => this.state.get().phases,
980
+ current: () => this.state.get().phases.find((p) => p.status !== "completed" && p.status !== "cancelled"),
981
+ completed: () => this.state.get().phases.filter((p) => p.status === "completed"),
982
+ get: (id) => this.state.get().phases.find((p) => p.id === id),
983
+ count: () => this.state.get().phases.length,
984
+ allCompleted: () => this.state.get().phases.length > 0 && this.state.get().phases.every((p) => p.status === "completed"),
985
+ onChange: (cb) => this.state.onPhaseChange(cb)
986
+ };
807
987
  wait = {
808
988
  generationStarted: (options = {}) => this.waitForGenerationStarted(options),
809
989
  generationComplete: (options = {}) => this.waitForGenerationComplete(options),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cf-vibesdk/sdk",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -22,7 +22,8 @@
22
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",
23
23
  "typecheck": "tsc -p ./tsconfig.json --noEmit",
24
24
  "test": "bun test test/*.test.ts",
25
- "test:integration": "bun test --timeout 600000 test/integration/*.test.ts"
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"
26
27
  },
27
28
  "devDependencies": {
28
29
  "@cloudflare/workers-types": "^4.20241218.0",