Skip to content

Commit fc806b1

Browse files
Copilothotlong
andcommitted
feat: add @objectql/edge-adapter foundation package
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 95c68a1 commit fc806b1

9 files changed

Lines changed: 541 additions & 42 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "@objectql/edge-adapter",
3+
"version": "4.2.0",
4+
"description": "Edge runtime adapter for ObjectQL - runtime detection, capability validation, and driver binding resolution",
5+
"keywords": [
6+
"objectql",
7+
"edge",
8+
"runtime",
9+
"cloudflare",
10+
"deno",
11+
"vercel",
12+
"adapter"
13+
],
14+
"license": "MIT",
15+
"main": "dist/index.js",
16+
"types": "dist/index.d.ts",
17+
"exports": {
18+
".": {
19+
"types": "./dist/index.d.ts",
20+
"default": "./dist/index.js"
21+
}
22+
},
23+
"files": [
24+
"dist"
25+
],
26+
"scripts": {
27+
"build": "tsc",
28+
"test": "vitest run"
29+
},
30+
"dependencies": {
31+
"@objectql/types": "workspace:*"
32+
},
33+
"devDependencies": {
34+
"typescript": "^5.3.0",
35+
"vitest": "^1.0.4"
36+
}
37+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* ObjectQL
3+
* Copyright (c) 2026-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
import type { EdgeAdapterConfig, EdgeRuntime } from '@objectql/types';
10+
11+
/**
12+
* Default driver recommendations per runtime.
13+
*/
14+
const DEFAULT_BINDINGS: Readonly<Record<EdgeRuntime, string>> = {
15+
'cloudflare-workers': '@objectql/driver-sqlite-wasm',
16+
'deno-deploy': '@objectql/driver-pg-wasm',
17+
'vercel-edge': '@objectql/driver-memory',
18+
'bun': '@objectql/driver-sqlite-wasm',
19+
'node': '@objectql/driver-sql',
20+
};
21+
22+
/**
23+
* A fully resolved driver binding for a named datasource.
24+
*/
25+
export interface ResolvedBinding {
26+
readonly datasource: string;
27+
readonly driver: string;
28+
readonly binding?: string;
29+
readonly config: Record<string, unknown>;
30+
}
31+
32+
/**
33+
* Returns the recommended default driver for a given runtime.
34+
*/
35+
export function getDefaultDriver(runtime: EdgeRuntime): string {
36+
return DEFAULT_BINDINGS[runtime];
37+
}
38+
39+
/**
40+
* Resolves edge driver bindings from adapter configuration.
41+
*
42+
* If no explicit bindings are provided, a default binding is generated
43+
* using the recommended driver for the target runtime.
44+
*/
45+
export function resolveBindings(config: EdgeAdapterConfig): readonly ResolvedBinding[] {
46+
const bindings = config.bindings ?? {};
47+
const resolved: ResolvedBinding[] = [];
48+
49+
for (const [datasource, binding] of Object.entries(bindings)) {
50+
resolved.push({
51+
datasource,
52+
driver: binding.driver,
53+
binding: binding.binding,
54+
config: binding.config ?? {},
55+
});
56+
}
57+
58+
// If no bindings provided, resolve a default
59+
if (resolved.length === 0) {
60+
resolved.push({
61+
datasource: 'default',
62+
driver: getDefaultDriver(config.runtime),
63+
config: {},
64+
});
65+
}
66+
67+
return resolved;
68+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* ObjectQL
3+
* Copyright (c) 2026-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
import type { EdgeCapabilities, EdgeRuntime } from '@objectql/types';
10+
import { EDGE_CAPABILITIES } from '@objectql/types';
11+
12+
/**
13+
* Declares the minimum capabilities a runtime must provide.
14+
*/
15+
export interface CapabilityRequirement {
16+
readonly wasm?: boolean;
17+
readonly persistentStorage?: boolean;
18+
readonly webSocket?: boolean;
19+
readonly scheduledTriggers?: boolean;
20+
readonly minExecutionTime?: number;
21+
}
22+
23+
/**
24+
* Result of validating a runtime against a set of requirements.
25+
*/
26+
export interface CapabilityValidationResult {
27+
readonly valid: boolean;
28+
readonly runtime: EdgeRuntime;
29+
readonly capabilities: EdgeCapabilities;
30+
readonly missing: readonly string[];
31+
}
32+
33+
/**
34+
* Returns the predefined capability profile for a given runtime.
35+
*/
36+
export function getCapabilities(runtime: EdgeRuntime): EdgeCapabilities {
37+
return EDGE_CAPABILITIES[runtime];
38+
}
39+
40+
/**
41+
* Validates that a runtime satisfies the given capability requirements.
42+
*/
43+
export function validateCapabilities(
44+
runtime: EdgeRuntime,
45+
requirements: CapabilityRequirement,
46+
): CapabilityValidationResult {
47+
const capabilities = getCapabilities(runtime);
48+
const missing: string[] = [];
49+
50+
if (requirements.wasm && !capabilities.wasm) {
51+
missing.push('WebAssembly');
52+
}
53+
if (requirements.persistentStorage && !capabilities.persistentStorage) {
54+
missing.push('Persistent Storage');
55+
}
56+
if (requirements.webSocket && !capabilities.webSocket) {
57+
missing.push('WebSocket');
58+
}
59+
if (requirements.scheduledTriggers && !capabilities.scheduledTriggers) {
60+
missing.push('Scheduled Triggers');
61+
}
62+
if (
63+
requirements.minExecutionTime &&
64+
capabilities.maxExecutionTime !== undefined &&
65+
capabilities.maxExecutionTime !== Infinity &&
66+
capabilities.maxExecutionTime < requirements.minExecutionTime
67+
) {
68+
missing.push(
69+
`Execution time (need ${requirements.minExecutionTime}ms, max ${capabilities.maxExecutionTime}ms)`,
70+
);
71+
}
72+
73+
return {
74+
valid: missing.length === 0,
75+
runtime,
76+
capabilities,
77+
missing,
78+
};
79+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* ObjectQL
3+
* Copyright (c) 2026-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
import type { EdgeRuntime } from '@objectql/types';
10+
11+
/**
12+
* Detects the current edge runtime environment by inspecting global objects.
13+
*
14+
* Detection order matters — more specific checks come first to avoid
15+
* false positives (e.g., Cloudflare Workers before generic edge checks).
16+
*/
17+
export function detectRuntime(): EdgeRuntime {
18+
// Check for Cloudflare Workers (has WebSocketPair in global scope)
19+
if (
20+
typeof globalThis !== 'undefined' &&
21+
'caches' in globalThis &&
22+
typeof (globalThis as Record<string, unknown>).WebSocketPair !== 'undefined'
23+
) {
24+
return 'cloudflare-workers';
25+
}
26+
27+
// Check for Deno
28+
if (typeof (globalThis as Record<string, unknown>).Deno !== 'undefined') {
29+
return 'deno-deploy';
30+
}
31+
32+
// Check for Bun
33+
if (typeof (globalThis as Record<string, unknown>).Bun !== 'undefined') {
34+
return 'bun';
35+
}
36+
37+
// Check for Vercel Edge Runtime
38+
if (typeof (globalThis as Record<string, unknown>).EdgeRuntime !== 'undefined') {
39+
return 'vercel-edge';
40+
}
41+
42+
// Default to Node.js
43+
return 'node';
44+
}

0 commit comments

Comments
 (0)