Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions docs/WORK_PLAN_2026_Q1_P2.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ export class MultiTenancyPlugin implements RuntimePlugin {
## Q3 — Edge Runtime & Offline Sync

> Status: **Planned** | Target: 2026-07 — 2026-09
> 📄 **Detailed Work Plan: [WORK_PLAN_2026_Q3.md](./WORK_PLAN_2026_Q3.md)**
> 🧩 **Type Contracts Defined:** `@objectql/types` — `sync.ts`, `edge.ts`

### Part A: Edge Runtime Support (4 weeks)

Expand Down
652 changes: 652 additions & 0 deletions docs/WORK_PLAN_2026_Q3.md

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions packages/foundation/types/src/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ export interface DriverCapabilities {
readonly connectionPooling?: boolean;
readonly preparedStatements?: boolean;
readonly queryCache?: boolean;

// Sync support (Q3 — Offline-First Sync Protocol)
/** Driver can record mutations to an append-only log for offline sync */
readonly mutationLog?: boolean;
/** Driver supports checkpoint-based change tracking */
readonly changeTracking?: boolean;
}

/**
Expand Down
157 changes: 157 additions & 0 deletions packages/foundation/types/src/edge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/**
* ObjectQL
* Copyright (c) 2026-present ObjectStack Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// ============================================================================
// Edge Runtime Types
// ============================================================================

/**
* Supported edge runtime environments.
*/
export type EdgeRuntime =
| 'cloudflare-workers'
| 'deno-deploy'
| 'vercel-edge'
| 'bun'
| 'node';

/**
* Edge-specific driver binding.
*
* Maps an ObjectQL driver to an edge-platform-native storage primitive.
* For example, Cloudflare Workers binds `driver-sqlite-wasm` to D1.
*/
export interface EdgeDriverBinding {
/**
* The ObjectQL driver package name.
* e.g., `'@objectql/driver-memory'`, `'@objectql/driver-sqlite-wasm'`
*/
readonly driver: string;

/**
* Edge-platform binding name (environment variable or resource identifier).
* e.g., `'D1_DATABASE'` for Cloudflare D1, `'POSTGRES_URL'` for Deno Postgres.
*/
readonly binding?: string;

/**
* Driver-specific configuration overrides for the edge environment.
*/
readonly config?: Record<string, unknown>;
}

/**
* Edge runtime adapter configuration.
*
* Declares how ObjectQL should adapt to a specific edge runtime.
* Used by the edge adapter packages (e.g., `@objectql/adapter-cloudflare`).
*/
export interface EdgeAdapterConfig {
/** Target edge runtime */
readonly runtime: EdgeRuntime;

/**
* Driver bindings for this edge environment.
* Maps datasource names to edge-specific driver bindings.
*/
readonly bindings?: Record<string, EdgeDriverBinding>;

/**
* Maximum execution time in milliseconds.
* Edge runtimes impose strict CPU time limits.
* Default varies by runtime (e.g., 30000 for Cloudflare Workers).
*/
readonly maxExecutionTime?: number;

/**
* Enable request-scoped driver connections.
* Edge runtimes are stateless; connections are created per-request.
* Default: true
*/
readonly requestScoped?: boolean;
}

/**
* Edge runtime environment capabilities.
*
* Declares what platform APIs are available in the target edge runtime.
* Used by ObjectQL to select compatible code paths.
*/
export interface EdgeCapabilities {
/** WebAssembly support */
readonly wasm: boolean;

/** Persistent storage available (OPFS, KV, D1, etc.) */
readonly persistentStorage: boolean;

/** WebSocket support for real-time sync */
readonly webSocket: boolean;

/** Cron/scheduled trigger support */
readonly scheduledTriggers: boolean;

/** Maximum request body size in bytes */
readonly maxRequestBodySize?: number;

/** Maximum execution time in milliseconds */
readonly maxExecutionTime?: number;

/** Available storage primitives */
readonly storagePrimitives: readonly string[];
}

/**
* Predefined edge capability profiles for known runtimes.
*/
export const EDGE_CAPABILITIES: Readonly<Record<EdgeRuntime, EdgeCapabilities>> = {
'cloudflare-workers': {
wasm: true,
persistentStorage: true,
webSocket: true,
scheduledTriggers: true,
maxRequestBodySize: 100 * 1024 * 1024, // 100MB
maxExecutionTime: 30_000,
storagePrimitives: ['D1', 'KV', 'R2', 'Durable Objects'],
},
'deno-deploy': {
wasm: true,
persistentStorage: true,
webSocket: true,
scheduledTriggers: true,
maxRequestBodySize: 512 * 1024, // 512KB
maxExecutionTime: 50_000,
storagePrimitives: ['Deno KV', 'Deno Postgres'],
},
'vercel-edge': {
wasm: true,
persistentStorage: false,
webSocket: false,
scheduledTriggers: false,
maxRequestBodySize: 4 * 1024 * 1024, // 4MB
maxExecutionTime: 25_000,
storagePrimitives: ['KV (via Vercel KV)', 'Postgres (via Vercel Postgres)'],
},
'bun': {
wasm: true,
persistentStorage: true,
webSocket: true,
scheduledTriggers: false,
maxRequestBodySize: Infinity,
maxExecutionTime: Infinity,
storagePrimitives: ['SQLite (bun:sqlite)', 'File System'],
Comment on lines +142 to +146
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Infinity for maxRequestBodySize / maxExecutionTime can break serialization (JSON.stringify(Infinity) becomes null) and makes “bytes/ms” semantics ambiguous. Prefer omitting these fields for “unlimited”, or use a documented sentinel value that round-trips in JSON.

Copilot uses AI. Check for mistakes.
},
'node': {
wasm: true,
persistentStorage: true,
webSocket: true,
scheduledTriggers: false,
maxRequestBodySize: Infinity,
maxExecutionTime: Infinity,
storagePrimitives: ['All drivers'],
Comment on lines +151 to +155
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as Bun: Infinity in the exported EDGE_CAPABILITIES profile won’t round-trip through JSON and may cause unexpected comparisons. Prefer leaving these limits undefined (unbounded) or using a JSON-safe sentinel.

Copilot uses AI. Check for mistakes.
},
};
2 changes: 2 additions & 0 deletions packages/foundation/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ export * from './plugin';
export * from './gateway';
export * from './logger';
export * from './ai';
export * from './sync';
export * from './edge';
15 changes: 15 additions & 0 deletions packages/foundation/types/src/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { z } from 'zod';
import { FieldConfig } from './field';
import { ActionConfig } from './action';
import { AnyValidationRule } from './validation';
import { SyncConfig } from './sync';

/**
* Re-export Protocol Types from @objectstack/spec 1.1.0
Expand Down Expand Up @@ -170,6 +171,20 @@ export interface ObjectConfig {
validation_strategy?: string;
};
};

/**
* Offline-First Sync configuration (RUNTIME ONLY).
* Opt-in per object. See {@link SyncConfig} for details.
*
* @example
* ```yaml
* sync:
* enabled: true
* strategy: last-write-wins
* conflict_fields: [status]
* ```
*/
sync?: SyncConfig;
}

/**
Expand Down
Loading
Loading