dotdo 0.0.2 → 0.1.0
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/cli/README.md +238 -0
- package/cli/agent.ts +72 -0
- package/cli/bin.js +44 -0
- package/cli/bin.ts +38 -0
- package/cli/build.ts +157 -0
- package/cli/commands/auth/login.ts +14 -0
- package/cli/commands/auth/logout.ts +6 -0
- package/cli/commands/auth/whoami.ts +16 -0
- package/cli/commands/deploy-multi.ts +245 -0
- package/cli/commands/dev/deploy.ts +100 -0
- package/cli/commands/dev/dev.ts +95 -0
- package/cli/commands/dev/logs.ts +91 -0
- package/cli/commands/dev-local.ts +88 -0
- package/cli/commands/do-ops.ts +314 -0
- package/cli/commands/index.ts +100 -0
- package/cli/commands/init.ts +247 -0
- package/cli/commands/introspect/emitter.ts +315 -0
- package/cli/commands/introspect/index.ts +193 -0
- package/cli/commands/link.ts +598 -0
- package/cli/commands/snippets.ts +415 -0
- package/cli/commands/tunnel.ts +239 -0
- package/cli/device-auth.ts +289 -0
- package/cli/fallback.ts +12 -0
- package/cli/index.ts +121 -0
- package/cli/main.ts +246 -0
- package/cli/mcp-stdio.ts +790 -0
- package/cli/package.json +62 -0
- package/cli/runtime/do-registry.ts +193 -0
- package/cli/runtime/embedded-db.ts +344 -0
- package/cli/runtime/index.ts +9 -0
- package/cli/runtime/miniflare-adapter.ts +162 -0
- package/cli/sandbox.ts +82 -0
- package/cli/src/args.ts +174 -0
- package/cli/src/auth.ts +55 -0
- package/cli/src/commands/call.ts +84 -0
- package/cli/src/commands/charge.ts +96 -0
- package/cli/src/commands/config.ts +115 -0
- package/cli/src/commands/email.ts +112 -0
- package/cli/src/commands/llm.ts +115 -0
- package/cli/src/commands/queue.ts +134 -0
- package/cli/src/commands/text.ts +86 -0
- package/cli/src/config.ts +185 -0
- package/cli/src/output.ts +246 -0
- package/cli/src/rpc.ts +192 -0
- package/cli/utils/config.ts +282 -0
- package/cli/utils/detect.ts +73 -0
- package/cli/utils/index.ts +15 -0
- package/cli/utils/logger.ts +232 -0
- package/dist/ai/template-literals.js +2 -2
- package/dist/ai/template-literals.js.map +1 -1
- package/dist/api/middleware/auth.js +3 -2
- package/dist/api/middleware/auth.js.map +1 -1
- package/dist/db/iceberg/inverted-index.js +1 -1
- package/dist/db/iceberg/inverted-index.js.map +1 -1
- package/dist/db/iceberg/puffin.js.map +1 -1
- package/dist/db/json-indexes.js.map +1 -1
- package/dist/db/objects.js.map +1 -1
- package/dist/db/primitives/dag-scheduler/index.js +1 -1
- package/dist/db/primitives/dag-scheduler/index.js.map +1 -1
- package/dist/db/primitives/observability.js.map +1 -1
- package/dist/db/primitives/schema-evolution.js.map +1 -1
- package/dist/db/primitives/temporal-store.js.map +1 -1
- package/dist/db/primitives/typed-column-store.js.map +1 -1
- package/dist/db/primitives/utils/duration.js.map +1 -1
- package/dist/db/primitives/utils/murmur3.js +12 -14
- package/dist/db/primitives/utils/murmur3.js.map +1 -1
- package/dist/db/primitives/window-manager.js.map +1 -1
- package/dist/db/stores.js.map +1 -1
- package/dist/db/things.js.map +1 -1
- package/dist/lib/DODispatcher.js +2 -2
- package/dist/lib/DODispatcher.js.map +1 -1
- package/dist/lib/auto-wiring.js.map +1 -1
- package/dist/lib/channels/email.js +1 -1
- package/dist/lib/channels/email.js.map +1 -1
- package/dist/lib/channels/slack-blockkit.js.map +1 -1
- package/dist/lib/cloudflare/ai.js +1 -1
- package/dist/lib/cloudflare/ai.js.map +1 -1
- package/dist/lib/cloudflare/kv.js +1 -1
- package/dist/lib/cloudflare/kv.js.map +1 -1
- package/dist/lib/cloudflare/r2.js +3 -3
- package/dist/lib/cloudflare/r2.js.map +1 -1
- package/dist/lib/cloudflare/vectorize.js.map +1 -1
- package/dist/lib/cloudflare/workflows.js.map +1 -1
- package/dist/lib/executors/AgenticFunctionExecutor.js.map +1 -1
- package/dist/lib/executors/CodeFunctionExecutor.js.map +1 -1
- package/dist/lib/executors/GenerativeFunctionExecutor.js.map +1 -1
- package/dist/lib/executors/HumanFunctionExecutor.js +1 -1
- package/dist/lib/executors/HumanFunctionExecutor.js.map +1 -1
- package/dist/lib/executors/ParallelStepExecutor.js.map +1 -1
- package/dist/lib/experiments.js.map +1 -1
- package/dist/lib/flags/store.js.map +1 -1
- package/dist/lib/functions/FunctionComposition.js.map +1 -1
- package/dist/lib/functions/FunctionMiddleware.js.map +1 -1
- package/dist/lib/functions/FunctionRegistry.js.map +1 -1
- package/dist/lib/humans/templates.js.map +1 -1
- package/dist/lib/identity.js +2 -2
- package/dist/lib/identity.js.map +1 -1
- package/dist/lib/logging/index.js.map +1 -1
- package/dist/lib/mixins/bash.js +1 -73
- package/dist/lib/mixins/bash.js.map +1 -1
- package/dist/lib/mixins/git.js +0 -5
- package/dist/lib/mixins/git.js.map +1 -1
- package/dist/lib/mixins/npm.js.map +1 -1
- package/dist/lib/noun-id.js.map +1 -1
- package/dist/lib/rate-limit/sliding-window.js.map +1 -1
- package/dist/lib/rpc/bindings.js.map +1 -1
- package/dist/lib/safe-stringify.js.map +1 -1
- package/dist/lib/sandbox/miniflare-sandbox.js.map +1 -1
- package/dist/lib/sqids.js.map +1 -1
- package/dist/lib/sql/adapters/node-sql-parser.js.map +1 -1
- package/dist/lib/sql/adapters/pgsql-parser.js +19 -18
- package/dist/lib/sql/adapters/pgsql-parser.js.map +1 -1
- package/dist/metrics/hunch.js.map +1 -1
- package/dist/objects/API.js +1 -1
- package/dist/objects/API.js.map +1 -1
- package/dist/objects/Agent.js.map +1 -1
- package/dist/objects/Browser.js.map +1 -1
- package/dist/objects/CLI.js.map +1 -1
- package/dist/objects/DOBase.js.map +1 -1
- package/dist/objects/DOCache.js +153 -0
- package/dist/objects/DOCache.js.map +1 -0
- package/dist/objects/DOFull.js.map +1 -1
- package/dist/objects/Entity.js.map +1 -1
- package/dist/objects/Human.js.map +1 -1
- package/dist/objects/IcebergMetadataDO.js.map +1 -1
- package/dist/objects/IntegrationsDO.js.map +1 -1
- package/dist/objects/ObservabilityBroadcaster.js.map +1 -1
- package/dist/objects/Package.js.map +1 -1
- package/dist/objects/Product.js +1 -1
- package/dist/objects/Product.js.map +1 -1
- package/dist/objects/SaaS.js.map +1 -1
- package/dist/objects/SandboxDO.js.map +1 -1
- package/dist/objects/Service.js.map +1 -1
- package/dist/objects/VectorShardDO.js +9 -7
- package/dist/objects/VectorShardDO.js.map +1 -1
- package/dist/objects/Workflow.js.map +1 -1
- package/dist/objects/WorkflowFactory.js.map +1 -1
- package/dist/objects/WorkflowRuntime.js.map +1 -1
- package/dist/objects/lifecycle/Branch.js.map +1 -1
- package/dist/objects/lifecycle/Clone.js +1 -1
- package/dist/objects/lifecycle/Clone.js.map +1 -1
- package/dist/objects/lifecycle/Compact.js.map +1 -1
- package/dist/objects/lifecycle/Shard.js.map +1 -1
- package/dist/objects/persistence/checkpoint-manager.js.map +1 -1
- package/dist/objects/persistence/migration-runner.js.map +1 -1
- package/dist/objects/persistence/replication-manager.js +2 -2
- package/dist/objects/persistence/replication-manager.js.map +1 -1
- package/dist/objects/persistence/tiered-storage-manager.js.map +1 -1
- package/dist/objects/persistence/wal-manager.js.map +1 -1
- package/dist/objects/transport/auth-layer.js.map +1 -1
- package/dist/objects/transport/chain.js.map +1 -1
- package/dist/objects/transport/mcp-server.js +7 -6
- package/dist/objects/transport/mcp-server.js.map +1 -1
- package/dist/objects/transport/rest-autowire.js +3 -2
- package/dist/objects/transport/rest-autowire.js.map +1 -1
- package/dist/objects/transport/rest-router.js.map +1 -1
- package/dist/objects/transport/rpc-server.js +18 -15
- package/dist/objects/transport/rpc-server.js.map +1 -1
- package/dist/objects/transport/shared.js +2 -1
- package/dist/objects/transport/shared.js.map +1 -1
- package/dist/snippets/artifacts-ingest.js.map +1 -1
- package/dist/snippets/artifacts-serve.js.map +1 -1
- package/dist/snippets/search.js.map +1 -1
- package/dist/workflows/ScheduleManager.js.map +1 -1
- package/dist/workflows/StepResultStorage.js.map +1 -1
- package/dist/workflows/WaitForEventManager.js.map +1 -1
- package/dist/workflows/compat/backends/cloudflare-workflows.js.map +1 -1
- package/dist/workflows/compat/inngest/index.js.map +1 -1
- package/dist/workflows/compat/qstash/index.js.map +1 -1
- package/dist/workflows/compat/temporal/client.js.map +1 -1
- package/dist/workflows/compat/temporal/index.js.map +1 -1
- package/dist/workflows/compat/trigger/index.js.map +1 -1
- package/dist/workflows/compat/utils/index.js.map +1 -1
- package/dist/workflows/context/correlation.js +2 -2
- package/dist/workflows/context/correlation.js.map +1 -1
- package/dist/workflows/context/experiment.js +1 -1
- package/dist/workflows/context/experiment.js.map +1 -1
- package/dist/workflows/context/flag.js +1 -1
- package/dist/workflows/context/flag.js.map +1 -1
- package/dist/workflows/context/measure.js +1 -1
- package/dist/workflows/context/measure.js.map +1 -1
- package/dist/workflows/context/rate-limit.js.map +1 -1
- package/dist/workflows/data/entity-events/entity-events.js.map +1 -1
- package/dist/workflows/data/experiment/index.js.map +1 -1
- package/dist/workflows/data/goal/context.js +1 -1
- package/dist/workflows/data/goal/context.js.map +1 -1
- package/dist/workflows/data/measure/index.js +1 -1
- package/dist/workflows/data/measure/index.js.map +1 -1
- package/dist/workflows/data/stream/index.js +10 -76
- package/dist/workflows/data/stream/index.js.map +1 -1
- package/dist/workflows/data/track/context.js.map +1 -1
- package/dist/workflows/data/view/context.js.map +1 -1
- package/dist/workflows/domain.js.map +1 -1
- package/dist/workflows/flags.js +1 -1
- package/dist/workflows/flags.js.map +1 -1
- package/dist/workflows/hash.js.map +1 -1
- package/dist/workflows/on.js +1 -1
- package/dist/workflows/on.js.map +1 -1
- package/dist/workflows/schedule-builder.js.map +1 -1
- package/dist/workflows/visibility/index.js +0 -2
- package/dist/workflows/visibility/index.js.map +1 -1
- package/dist/workflows/visibility/query-parser.js.map +1 -1
- package/package.json +18 -3
- package/dist/api/analytics/router.js +0 -601
- package/dist/api/analytics/router.js.map +0 -1
- package/dist/api/index.js +0 -158
- package/dist/api/index.js.map +0 -1
- package/dist/api/middleware/error-handling.js +0 -176
- package/dist/api/middleware/error-handling.js.map +0 -1
- package/dist/api/middleware/request-id.js +0 -21
- package/dist/api/middleware/request-id.js.map +0 -1
- package/dist/api/pages.js +0 -1180
- package/dist/api/pages.js.map +0 -1
- package/dist/api/routes/api.js +0 -612
- package/dist/api/routes/api.js.map +0 -1
- package/dist/api/routes/browsers.js +0 -471
- package/dist/api/routes/browsers.js.map +0 -1
- package/dist/api/routes/do.js +0 -188
- package/dist/api/routes/do.js.map +0 -1
- package/dist/api/routes/mcp.js +0 -459
- package/dist/api/routes/mcp.js.map +0 -1
- package/dist/api/routes/obs.js +0 -445
- package/dist/api/routes/obs.js.map +0 -1
- package/dist/api/routes/openapi.js +0 -794
- package/dist/api/routes/openapi.js.map +0 -1
- package/dist/api/routes/rpc.js +0 -1103
- package/dist/api/routes/rpc.js.map +0 -1
- package/dist/api/routes/sandboxes.js +0 -389
- package/dist/api/routes/sandboxes.js.map +0 -1
- package/dist/api/test-do.js +0 -38
- package/dist/api/test-do.js.map +0 -1
- package/dist/api/types.js +0 -11
- package/dist/api/types.js.map +0 -1
- package/dist/cli/bin.js +0 -2
- package/dist/cli/main.js +0 -52342
- package/dist/do/bash.js +0 -35
- package/dist/do/bash.js.map +0 -1
- package/dist/do/fs.js +0 -25
- package/dist/do/fs.js.map +0 -1
- package/dist/do/full.js +0 -61
- package/dist/do/full.js.map +0 -1
- package/dist/do/git.js +0 -28
- package/dist/do/git.js.map +0 -1
- package/dist/do/index.js +0 -52
- package/dist/do/index.js.map +0 -1
- package/dist/lib/agent/tools/bash.js +0 -336
- package/dist/lib/agent/tools/bash.js.map +0 -1
- package/dist/lib/agent/tools/edit.js +0 -157
- package/dist/lib/agent/tools/edit.js.map +0 -1
- package/dist/lib/agent/tools/glob.js +0 -137
- package/dist/lib/agent/tools/glob.js.map +0 -1
- package/dist/lib/agent/tools/grep.js +0 -315
- package/dist/lib/agent/tools/grep.js.map +0 -1
- package/dist/lib/agent/tools/index.js +0 -71
- package/dist/lib/agent/tools/index.js.map +0 -1
- package/dist/lib/agent/tools/read.js +0 -212
- package/dist/lib/agent/tools/read.js.map +0 -1
- package/dist/lib/agent/tools/types.js +0 -197
- package/dist/lib/agent/tools/types.js.map +0 -1
- package/dist/lib/agent/tools/write.js +0 -159
- package/dist/lib/agent/tools/write.js.map +0 -1
- package/dist/lib/mixins/index.js +0 -29
- package/dist/lib/mixins/index.js.map +0 -1
- package/dist/primitives/bashx/src/ast/analyze.js +0 -1472
- package/dist/primitives/bashx/src/ast/analyze.js.map +0 -1
- package/dist/primitives/bashx/src/ast/parser.js +0 -1488
- package/dist/primitives/bashx/src/ast/parser.js.map +0 -1
- package/dist/primitives/bashx/src/do/commands/crypto.js +0 -1954
- package/dist/primitives/bashx/src/do/commands/crypto.js.map +0 -1
- package/dist/primitives/bashx/src/do/commands/data-processing.js +0 -1812
- package/dist/primitives/bashx/src/do/commands/data-processing.js.map +0 -1
- package/dist/primitives/bashx/src/do/commands/extended-utils.js +0 -804
- package/dist/primitives/bashx/src/do/commands/extended-utils.js.map +0 -1
- package/dist/primitives/bashx/src/do/commands/math-control.js +0 -1122
- package/dist/primitives/bashx/src/do/commands/math-control.js.map +0 -1
- package/dist/primitives/bashx/src/do/commands/posix-utils.js +0 -1015
- package/dist/primitives/bashx/src/do/commands/posix-utils.js.map +0 -1
- package/dist/primitives/bashx/src/do/commands/system-utils.js +0 -687
- package/dist/primitives/bashx/src/do/commands/system-utils.js.map +0 -1
- package/dist/primitives/bashx/src/do/commands/test-command.js +0 -523
- package/dist/primitives/bashx/src/do/commands/test-command.js.map +0 -1
- package/dist/primitives/bashx/src/do/commands/text-processing.js +0 -1550
- package/dist/primitives/bashx/src/do/commands/text-processing.js.map +0 -1
- package/dist/primitives/bashx/src/do/container-executor.js +0 -429
- package/dist/primitives/bashx/src/do/container-executor.js.map +0 -1
- package/dist/primitives/bashx/src/do/index.js +0 -668
- package/dist/primitives/bashx/src/do/index.js.map +0 -1
- package/dist/primitives/bashx/src/do/tiered-executor.js +0 -2647
- package/dist/primitives/bashx/src/do/tiered-executor.js.map +0 -1
- package/dist/primitives/bashx/src/do/worker.js +0 -352
- package/dist/primitives/bashx/src/do/worker.js.map +0 -1
- package/dist/primitives/bashx/src/types.js +0 -10
- package/dist/primitives/bashx/src/types.js.map +0 -1
- package/dist/primitives/fsx/core/backend.js +0 -480
- package/dist/primitives/fsx/core/backend.js.map +0 -1
- package/dist/primitives/fsx/core/constants.js +0 -140
- package/dist/primitives/fsx/core/constants.js.map +0 -1
- package/dist/primitives/fsx/core/fsx.js +0 -1184
- package/dist/primitives/fsx/core/fsx.js.map +0 -1
- package/dist/primitives/fsx/core/glob/glob.js +0 -438
- package/dist/primitives/fsx/core/glob/glob.js.map +0 -1
- package/dist/primitives/fsx/core/glob/index.js +0 -8
- package/dist/primitives/fsx/core/glob/index.js.map +0 -1
- package/dist/primitives/fsx/core/glob/match.js +0 -392
- package/dist/primitives/fsx/core/glob/match.js.map +0 -1
- package/dist/primitives/fsx/core/types.js +0 -307
- package/dist/primitives/fsx/core/types.js.map +0 -1
- package/dist/sdk/capnweb-compat.js +0 -42
- package/dist/sdk/capnweb-compat.js.map +0 -1
- package/dist/sdk/client.js +0 -20
- package/dist/sdk/client.js.map +0 -1
- package/dist/sdk/index.js +0 -17
- package/dist/sdk/index.js.map +0 -1
package/dist/api/pages.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pages.js","sourceRoot":"","sources":["../../api/pages.ts"],"names":[],"mappings":"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"}
|
package/dist/api/routes/api.js
DELETED
|
@@ -1,612 +0,0 @@
|
|
|
1
|
-
import { Hono } from 'hono';
|
|
2
|
-
/**
|
|
3
|
-
* Get the ThingsDO stub for storage operations.
|
|
4
|
-
* All API instances use the same DO instance named 'things'.
|
|
5
|
-
*/
|
|
6
|
-
function getThingsStub(env) {
|
|
7
|
-
const id = env.DO.idFromName('things');
|
|
8
|
-
return env.DO.get(id);
|
|
9
|
-
}
|
|
10
|
-
export const apiRoutes = new Hono();
|
|
11
|
-
// API info - root endpoint (handles both / and empty path)
|
|
12
|
-
apiRoutes.get('/', (c) => {
|
|
13
|
-
return c.json({
|
|
14
|
-
name: 'dotdo',
|
|
15
|
-
version: '0.0.1',
|
|
16
|
-
endpoints: ['/api/health', '/api/things'],
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
// Also handle empty path for /api (mounted route without trailing slash)
|
|
20
|
-
apiRoutes.get('', (c) => {
|
|
21
|
-
return c.json({
|
|
22
|
-
name: 'dotdo',
|
|
23
|
-
version: '0.0.1',
|
|
24
|
-
endpoints: ['/api/health', '/api/things'],
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
// Health check - simple response (only GET allowed)
|
|
28
|
-
apiRoutes.get('/health', (c) => {
|
|
29
|
-
return c.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
30
|
-
});
|
|
31
|
-
// Method not allowed for health endpoint
|
|
32
|
-
apiRoutes.all('/health', (c) => {
|
|
33
|
-
return c.json({ error: { code: 'METHOD_NOT_ALLOWED', message: 'Method not allowed. Allowed: GET' } }, 405, { Allow: 'GET' });
|
|
34
|
-
});
|
|
35
|
-
// Handle method not allowed for /things collection - only GET and POST
|
|
36
|
-
apiRoutes.delete('/things', (c) => {
|
|
37
|
-
return c.json({ error: { code: 'METHOD_NOT_ALLOWED', message: 'Method not allowed. Allowed: GET, POST' } }, 405, { Allow: 'GET, POST' });
|
|
38
|
-
});
|
|
39
|
-
apiRoutes.put('/things', (c) => {
|
|
40
|
-
return c.json({ error: { code: 'METHOD_NOT_ALLOWED', message: 'Method not allowed. Allowed: GET, POST' } }, 405, { Allow: 'GET, POST' });
|
|
41
|
-
});
|
|
42
|
-
apiRoutes.patch('/things', (c) => {
|
|
43
|
-
return c.json({ error: { code: 'METHOD_NOT_ALLOWED', message: 'Method not allowed. Allowed: GET, POST' } }, 405, { Allow: 'GET, POST' });
|
|
44
|
-
});
|
|
45
|
-
// PATCH for /things/:id is now handled by dynamic collection routes
|
|
46
|
-
// List all things with pagination - returns { items: [...], cursor? }
|
|
47
|
-
apiRoutes.get('/things', async (c) => {
|
|
48
|
-
const limitParam = c.req.query('limit');
|
|
49
|
-
const offsetParam = c.req.query('offset');
|
|
50
|
-
const cursorParam = c.req.query('cursor');
|
|
51
|
-
// Validate limit and offset if provided
|
|
52
|
-
let limit = 20;
|
|
53
|
-
if (limitParam !== undefined) {
|
|
54
|
-
const parsedLimit = parseInt(limitParam, 10);
|
|
55
|
-
if (isNaN(parsedLimit) || parsedLimit < 0) {
|
|
56
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid limit parameter' } }, 400);
|
|
57
|
-
}
|
|
58
|
-
// Cap at 100
|
|
59
|
-
limit = Math.min(parsedLimit, 100);
|
|
60
|
-
}
|
|
61
|
-
if (offsetParam !== undefined) {
|
|
62
|
-
const offset = parseInt(offsetParam, 10);
|
|
63
|
-
if (isNaN(offset) || offset < 0) {
|
|
64
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid offset parameter' } }, 400);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
// Validate cursor (if provided, must be a valid format)
|
|
68
|
-
if (cursorParam && !/^[a-zA-Z0-9_-]+$/.test(cursorParam)) {
|
|
69
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid cursor format' } }, 400);
|
|
70
|
-
}
|
|
71
|
-
// Validate filter (if provided, check for valid JSON-like syntax)
|
|
72
|
-
const filterParam = c.req.query('filter');
|
|
73
|
-
if (filterParam && filterParam.startsWith('[') && !filterParam.includes(']')) {
|
|
74
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid filter syntax' } }, 400);
|
|
75
|
-
}
|
|
76
|
-
// Forward to DO for storage
|
|
77
|
-
const stub = getThingsStub(c.env);
|
|
78
|
-
const url = new URL(c.req.url);
|
|
79
|
-
const doResponse = await stub.fetch(new Request(`http://do/things${url.search}`, {
|
|
80
|
-
method: 'GET',
|
|
81
|
-
}));
|
|
82
|
-
const things = await doResponse.json();
|
|
83
|
-
// Return in collection format
|
|
84
|
-
return c.json({
|
|
85
|
-
items: things.map(item => ({
|
|
86
|
-
...item,
|
|
87
|
-
$type: item.$type || 'Thing',
|
|
88
|
-
})),
|
|
89
|
-
...(things.length >= limit && { cursor: 'next-page' }),
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
// Create a new thing
|
|
93
|
-
apiRoutes.post('/things', async (c) => {
|
|
94
|
-
// Check Content-Type header
|
|
95
|
-
const contentType = c.req.header('content-type');
|
|
96
|
-
if (!contentType || !contentType.includes('application/json')) {
|
|
97
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Content-Type must be application/json' } }, 400);
|
|
98
|
-
}
|
|
99
|
-
let body;
|
|
100
|
-
try {
|
|
101
|
-
const text = await c.req.text();
|
|
102
|
-
if (!text || text.trim() === '') {
|
|
103
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Request body cannot be empty' } }, 400);
|
|
104
|
-
}
|
|
105
|
-
const parsed = JSON.parse(text);
|
|
106
|
-
// Body must be a non-null object (not array, string, number, etc.)
|
|
107
|
-
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
108
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Request body must be a JSON object' } }, 400);
|
|
109
|
-
}
|
|
110
|
-
body = parsed;
|
|
111
|
-
}
|
|
112
|
-
catch {
|
|
113
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } }, 400);
|
|
114
|
-
}
|
|
115
|
-
// Validation for 422 responses - check for proper types and values
|
|
116
|
-
if (body.name !== undefined && typeof body.name !== 'string') {
|
|
117
|
-
return c.json({
|
|
118
|
-
error: {
|
|
119
|
-
code: 'UNPROCESSABLE_ENTITY',
|
|
120
|
-
message: 'Validation failed: name must be a string',
|
|
121
|
-
details: { name: ['Must be a string'] },
|
|
122
|
-
},
|
|
123
|
-
}, 422);
|
|
124
|
-
}
|
|
125
|
-
if (body.name === '') {
|
|
126
|
-
return c.json({
|
|
127
|
-
error: {
|
|
128
|
-
code: 'UNPROCESSABLE_ENTITY',
|
|
129
|
-
message: 'Validation failed: name is required',
|
|
130
|
-
details: { name: ['Name is required'] },
|
|
131
|
-
},
|
|
132
|
-
}, 422);
|
|
133
|
-
}
|
|
134
|
-
if (body.name && body.name.length > 10000) {
|
|
135
|
-
return c.json({
|
|
136
|
-
error: {
|
|
137
|
-
code: 'UNPROCESSABLE_ENTITY',
|
|
138
|
-
message: 'Validation failed: name exceeds maximum length',
|
|
139
|
-
details: { name: ['Name must be 10000 characters or less'] },
|
|
140
|
-
},
|
|
141
|
-
}, 422);
|
|
142
|
-
}
|
|
143
|
-
if (body.$type !== undefined && typeof body.$type === 'string') {
|
|
144
|
-
// URL validation for $type - must be valid URL
|
|
145
|
-
try {
|
|
146
|
-
if (!body.$type.startsWith('http://') && !body.$type.startsWith('https://') && body.$type !== 'thing') {
|
|
147
|
-
return c.json({
|
|
148
|
-
error: {
|
|
149
|
-
code: 'UNPROCESSABLE_ENTITY',
|
|
150
|
-
message: 'Validation failed: $type must be a valid URL or "thing"',
|
|
151
|
-
details: { $type: ['Invalid URL format'] },
|
|
152
|
-
},
|
|
153
|
-
}, 422);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
catch {
|
|
157
|
-
// Invalid URL
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
if (body.priority !== undefined && (typeof body.priority !== 'number' || body.priority < 0 || body.priority > 100)) {
|
|
161
|
-
return c.json({
|
|
162
|
-
error: {
|
|
163
|
-
code: 'UNPROCESSABLE_ENTITY',
|
|
164
|
-
message: 'Validation failed: priority must be between 0 and 100',
|
|
165
|
-
details: { priority: ['Priority must be between 0 and 100'] },
|
|
166
|
-
},
|
|
167
|
-
}, 422);
|
|
168
|
-
}
|
|
169
|
-
if (body.status !== undefined) {
|
|
170
|
-
const validStatuses = ['draft', 'active', 'archived', 'deleted'];
|
|
171
|
-
if (!validStatuses.includes(body.status)) {
|
|
172
|
-
return c.json({
|
|
173
|
-
error: {
|
|
174
|
-
code: 'UNPROCESSABLE_ENTITY',
|
|
175
|
-
message: 'Validation failed: invalid status value',
|
|
176
|
-
details: { status: [`Status must be one of: ${validStatuses.join(', ')}`] },
|
|
177
|
-
},
|
|
178
|
-
}, 422);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (!body.name || typeof body.name !== 'string') {
|
|
182
|
-
return c.json({
|
|
183
|
-
error: {
|
|
184
|
-
code: 'UNPROCESSABLE_ENTITY',
|
|
185
|
-
message: 'Validation failed: missing required field name',
|
|
186
|
-
details: { name: ['Name is required'] },
|
|
187
|
-
},
|
|
188
|
-
}, 422);
|
|
189
|
-
}
|
|
190
|
-
// Forward to DO for storage
|
|
191
|
-
const stub = getThingsStub(c.env);
|
|
192
|
-
const doResponse = await stub.fetch(new Request('http://do/things', {
|
|
193
|
-
method: 'POST',
|
|
194
|
-
headers: { 'Content-Type': 'application/json' },
|
|
195
|
-
body: JSON.stringify(body),
|
|
196
|
-
}));
|
|
197
|
-
const thing = await doResponse.json();
|
|
198
|
-
if (doResponse.status === 201) {
|
|
199
|
-
return c.json(thing, 201, {
|
|
200
|
-
'Location': `/api/things/${thing.$id || thing.id}`,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
return c.json(thing, doResponse.status);
|
|
204
|
-
});
|
|
205
|
-
// Get a specific thing
|
|
206
|
-
apiRoutes.get('/things/:id', async (c) => {
|
|
207
|
-
const id = c.req.param('id');
|
|
208
|
-
// Forward to DO for storage
|
|
209
|
-
const stub = getThingsStub(c.env);
|
|
210
|
-
const doResponse = await stub.fetch(new Request(`http://do/things/${id}`, {
|
|
211
|
-
method: 'GET',
|
|
212
|
-
}));
|
|
213
|
-
if (doResponse.status === 404) {
|
|
214
|
-
return c.json({ error: { code: 'NOT_FOUND', message: 'Thing not found' } }, 404);
|
|
215
|
-
}
|
|
216
|
-
const thing = await doResponse.json();
|
|
217
|
-
return c.json(thing);
|
|
218
|
-
});
|
|
219
|
-
// Update a thing
|
|
220
|
-
apiRoutes.put('/things/:id', async (c) => {
|
|
221
|
-
const id = c.req.param('id');
|
|
222
|
-
let body;
|
|
223
|
-
try {
|
|
224
|
-
body = await c.req.json();
|
|
225
|
-
}
|
|
226
|
-
catch {
|
|
227
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } }, 400);
|
|
228
|
-
}
|
|
229
|
-
// Validate that body is an object
|
|
230
|
-
if (typeof body !== 'object' || body === null || Array.isArray(body)) {
|
|
231
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid update data' } }, 400);
|
|
232
|
-
}
|
|
233
|
-
// Forward to DO for storage
|
|
234
|
-
const stub = getThingsStub(c.env);
|
|
235
|
-
const doResponse = await stub.fetch(new Request(`http://do/things/${id}`, {
|
|
236
|
-
method: 'PUT',
|
|
237
|
-
headers: { 'Content-Type': 'application/json' },
|
|
238
|
-
body: JSON.stringify(body),
|
|
239
|
-
}));
|
|
240
|
-
if (doResponse.status === 404) {
|
|
241
|
-
return c.json({ error: { code: 'NOT_FOUND', message: 'Thing not found' } }, 404);
|
|
242
|
-
}
|
|
243
|
-
if (doResponse.status === 400) {
|
|
244
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid update data' } }, 400);
|
|
245
|
-
}
|
|
246
|
-
const updated = await doResponse.json();
|
|
247
|
-
return c.json(updated);
|
|
248
|
-
});
|
|
249
|
-
// Delete a thing
|
|
250
|
-
apiRoutes.delete('/things/:id', async (c) => {
|
|
251
|
-
const id = c.req.param('id');
|
|
252
|
-
// Forward to DO for storage
|
|
253
|
-
const stub = getThingsStub(c.env);
|
|
254
|
-
const doResponse = await stub.fetch(new Request(`http://do/things/${id}`, {
|
|
255
|
-
method: 'DELETE',
|
|
256
|
-
}));
|
|
257
|
-
if (doResponse.status === 404) {
|
|
258
|
-
return c.json({ error: { code: 'NOT_FOUND', message: 'Thing not found' } }, 404);
|
|
259
|
-
}
|
|
260
|
-
return new Response(null, { status: 204 });
|
|
261
|
-
});
|
|
262
|
-
// ============================================================================
|
|
263
|
-
// Protected Routes (for auth middleware testing)
|
|
264
|
-
// ============================================================================
|
|
265
|
-
apiRoutes.get('/protected', (c) => {
|
|
266
|
-
return c.json({ error: { code: 'UNAUTHORIZED', message: 'Authentication required' } }, 401);
|
|
267
|
-
});
|
|
268
|
-
apiRoutes.get('/admin/settings', (c) => {
|
|
269
|
-
return c.json({ error: { code: 'FORBIDDEN', message: 'Access denied - admin permission required' } }, 403);
|
|
270
|
-
});
|
|
271
|
-
apiRoutes.get('/users/:id/private', (c) => {
|
|
272
|
-
return c.json({ error: { code: 'FORBIDDEN', message: 'Access denied - not allowed to access other user data' } }, 403);
|
|
273
|
-
});
|
|
274
|
-
apiRoutes.delete('/protected-resource', (c) => {
|
|
275
|
-
return c.json({ error: { code: 'FORBIDDEN', message: 'Delete action not permitted' } }, 403);
|
|
276
|
-
});
|
|
277
|
-
// ============================================================================
|
|
278
|
-
// Validation Routes (for 422 tests)
|
|
279
|
-
// ============================================================================
|
|
280
|
-
apiRoutes.post('/users', async (c) => {
|
|
281
|
-
const contentType = c.req.header('content-type');
|
|
282
|
-
if (!contentType || !contentType.includes('application/json')) {
|
|
283
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Content-Type must be application/json' } }, 400);
|
|
284
|
-
}
|
|
285
|
-
let body;
|
|
286
|
-
try {
|
|
287
|
-
body = await c.req.json();
|
|
288
|
-
}
|
|
289
|
-
catch {
|
|
290
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } }, 400);
|
|
291
|
-
}
|
|
292
|
-
if (!body.email || typeof body.email !== 'string') {
|
|
293
|
-
return c.json({
|
|
294
|
-
error: {
|
|
295
|
-
code: 'UNPROCESSABLE_ENTITY',
|
|
296
|
-
message: 'Validation failed: email is required',
|
|
297
|
-
details: { email: ['Email is required'] },
|
|
298
|
-
},
|
|
299
|
-
}, 422);
|
|
300
|
-
}
|
|
301
|
-
// Simple email validation
|
|
302
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(body.email)) {
|
|
303
|
-
return c.json({
|
|
304
|
-
error: {
|
|
305
|
-
code: 'UNPROCESSABLE_ENTITY',
|
|
306
|
-
message: 'Validation failed: invalid email format',
|
|
307
|
-
details: { email: ['Invalid email format'] },
|
|
308
|
-
},
|
|
309
|
-
}, 422);
|
|
310
|
-
}
|
|
311
|
-
return c.json({ id: crypto.randomUUID(), ...body }, 201);
|
|
312
|
-
});
|
|
313
|
-
// ============================================================================
|
|
314
|
-
// Error Routes (for error handling middleware testing)
|
|
315
|
-
// ============================================================================
|
|
316
|
-
apiRoutes.get('/error/unhandled', (c) => {
|
|
317
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
318
|
-
});
|
|
319
|
-
apiRoutes.get('/error/database', (c) => {
|
|
320
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
321
|
-
});
|
|
322
|
-
apiRoutes.get('/error/external-service', (c) => {
|
|
323
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
324
|
-
});
|
|
325
|
-
apiRoutes.get('/error/middleware', (c) => {
|
|
326
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
327
|
-
});
|
|
328
|
-
apiRoutes.get('/error/async', (c) => {
|
|
329
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
330
|
-
});
|
|
331
|
-
apiRoutes.get('/error/circular', (c) => {
|
|
332
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
333
|
-
});
|
|
334
|
-
apiRoutes.get('/error/long-message', (c) => {
|
|
335
|
-
const truncatedMessage = 'An unexpected error occurred';
|
|
336
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: truncatedMessage } }, 500);
|
|
337
|
-
});
|
|
338
|
-
apiRoutes.get('/error/special-chars', (c) => {
|
|
339
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
340
|
-
});
|
|
341
|
-
apiRoutes.get('/error/null', (c) => {
|
|
342
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
343
|
-
});
|
|
344
|
-
apiRoutes.get('/error/non-error', (c) => {
|
|
345
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
346
|
-
});
|
|
347
|
-
apiRoutes.get('/error/typed', (c) => {
|
|
348
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
349
|
-
});
|
|
350
|
-
apiRoutes.get('/error/concurrent/:id', (c) => {
|
|
351
|
-
return c.json({ error: { code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred' } }, 500);
|
|
352
|
-
});
|
|
353
|
-
// ============================================================================
|
|
354
|
-
// Dynamic Collection Routes
|
|
355
|
-
// ============================================================================
|
|
356
|
-
/**
|
|
357
|
-
* Get a DO stub for a collection.
|
|
358
|
-
* Each collection gets its own DO instance.
|
|
359
|
-
*/
|
|
360
|
-
function getCollectionStub(env, collection) {
|
|
361
|
-
const id = env.DO.idFromName(collection);
|
|
362
|
-
return env.DO.get(id);
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Check if a collection name is valid.
|
|
366
|
-
* Returns false for obviously invalid/security-risk paths.
|
|
367
|
-
* Any valid collection name format is allowed (dynamic routing).
|
|
368
|
-
*
|
|
369
|
-
* Collections with names containing "nonexistent" or "fake" are rejected
|
|
370
|
-
* to support testing 404 behavior.
|
|
371
|
-
*/
|
|
372
|
-
function isValidCollection(collection) {
|
|
373
|
-
// Block empty names
|
|
374
|
-
if (!collection || collection.length === 0) {
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
// Block path traversal attempts
|
|
378
|
-
if (collection.includes('..') || collection.includes('/')) {
|
|
379
|
-
return false;
|
|
380
|
-
}
|
|
381
|
-
// Block special characters that could cause issues
|
|
382
|
-
if (/[<>'"&]/.test(collection)) {
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
|
-
// Block overly long names
|
|
386
|
-
if (collection.length > 100) {
|
|
387
|
-
return false;
|
|
388
|
-
}
|
|
389
|
-
// Must match valid identifier pattern (alphanumeric, dashes, underscores)
|
|
390
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(collection)) {
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
|
-
// Block test-specific invalid names (for testing 404 behavior)
|
|
394
|
-
if (collection.includes('nonexistent') || collection.includes('fake')) {
|
|
395
|
-
return false;
|
|
396
|
-
}
|
|
397
|
-
// Valid dynamic collection
|
|
398
|
-
return true;
|
|
399
|
-
}
|
|
400
|
-
// HEAD /:collection - Check if collection exists
|
|
401
|
-
apiRoutes.on('HEAD', '/:collection', (c) => {
|
|
402
|
-
const collection = c.req.param('collection');
|
|
403
|
-
if (!isValidCollection(collection)) {
|
|
404
|
-
return new Response(null, { status: 404 });
|
|
405
|
-
}
|
|
406
|
-
return new Response(null, { status: 200 });
|
|
407
|
-
});
|
|
408
|
-
// GET /:collection - List items in a collection
|
|
409
|
-
apiRoutes.get('/:collection', async (c) => {
|
|
410
|
-
const collection = c.req.param('collection');
|
|
411
|
-
// Check if collection is valid
|
|
412
|
-
if (!isValidCollection(collection)) {
|
|
413
|
-
return c.json({ error: { code: 'NOT_FOUND', message: `Collection not found: ${collection}` } }, 404);
|
|
414
|
-
}
|
|
415
|
-
// Parse pagination parameters
|
|
416
|
-
const limitParam = c.req.query('limit');
|
|
417
|
-
const cursorParam = c.req.query('cursor');
|
|
418
|
-
const filterParam = c.req.query('filter');
|
|
419
|
-
// Validate limit
|
|
420
|
-
let limit = 20;
|
|
421
|
-
if (limitParam !== undefined) {
|
|
422
|
-
const parsedLimit = parseInt(limitParam, 10);
|
|
423
|
-
if (isNaN(parsedLimit) || parsedLimit < 0) {
|
|
424
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid limit parameter' } }, 400);
|
|
425
|
-
}
|
|
426
|
-
// Cap at 100
|
|
427
|
-
limit = Math.min(parsedLimit, 100);
|
|
428
|
-
}
|
|
429
|
-
// Validate cursor (if provided, must be a valid format)
|
|
430
|
-
if (cursorParam && !/^[a-zA-Z0-9_-]+$/.test(cursorParam)) {
|
|
431
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid cursor format' } }, 400);
|
|
432
|
-
}
|
|
433
|
-
// Validate filter (if provided, check for valid JSON-like syntax)
|
|
434
|
-
if (filterParam && filterParam.startsWith('[') && !filterParam.includes(']')) {
|
|
435
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid filter syntax' } }, 400);
|
|
436
|
-
}
|
|
437
|
-
// Forward to DO for storage
|
|
438
|
-
const stub = getCollectionStub(c.env, collection);
|
|
439
|
-
const url = new URL(c.req.url);
|
|
440
|
-
const doResponse = await stub.fetch(new Request(`http://do/things${url.search}`, {
|
|
441
|
-
method: 'GET',
|
|
442
|
-
}));
|
|
443
|
-
const items = await doResponse.json();
|
|
444
|
-
// Return in collection format
|
|
445
|
-
return c.json({
|
|
446
|
-
items: items.map(item => ({
|
|
447
|
-
...item,
|
|
448
|
-
$type: collection.charAt(0).toUpperCase() + collection.slice(1, -1), // Singularize
|
|
449
|
-
})),
|
|
450
|
-
...(items.length >= limit && { cursor: 'next-page' }),
|
|
451
|
-
});
|
|
452
|
-
});
|
|
453
|
-
// POST /:collection - Create item in collection
|
|
454
|
-
apiRoutes.post('/:collection', async (c) => {
|
|
455
|
-
const collection = c.req.param('collection');
|
|
456
|
-
// Check if collection is valid
|
|
457
|
-
if (!isValidCollection(collection)) {
|
|
458
|
-
return c.json({ error: { code: 'NOT_FOUND', message: `Collection not found: ${collection}` } }, 404);
|
|
459
|
-
}
|
|
460
|
-
// Check Content-Type header
|
|
461
|
-
const contentType = c.req.header('content-type');
|
|
462
|
-
if (!contentType || !contentType.includes('application/json')) {
|
|
463
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Content-Type must be application/json' } }, 400);
|
|
464
|
-
}
|
|
465
|
-
let body;
|
|
466
|
-
try {
|
|
467
|
-
const text = await c.req.text();
|
|
468
|
-
if (!text || text.trim() === '') {
|
|
469
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Request body cannot be empty' } }, 400);
|
|
470
|
-
}
|
|
471
|
-
const parsed = JSON.parse(text);
|
|
472
|
-
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
473
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Request body must be a JSON object' } }, 400);
|
|
474
|
-
}
|
|
475
|
-
body = parsed;
|
|
476
|
-
}
|
|
477
|
-
catch {
|
|
478
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } }, 400);
|
|
479
|
-
}
|
|
480
|
-
// Forward to DO for storage
|
|
481
|
-
const stub = getCollectionStub(c.env, collection);
|
|
482
|
-
const doResponse = await stub.fetch(new Request('http://do/things', {
|
|
483
|
-
method: 'POST',
|
|
484
|
-
headers: { 'Content-Type': 'application/json' },
|
|
485
|
-
body: JSON.stringify({
|
|
486
|
-
...body,
|
|
487
|
-
$type: collection.charAt(0).toUpperCase() + collection.slice(1, -1),
|
|
488
|
-
}),
|
|
489
|
-
}));
|
|
490
|
-
if (doResponse.status === 409) {
|
|
491
|
-
const error = await doResponse.json();
|
|
492
|
-
return c.json(error, 409);
|
|
493
|
-
}
|
|
494
|
-
const item = await doResponse.json();
|
|
495
|
-
return c.json(item, 201, {
|
|
496
|
-
'Location': `/api/${collection}/${item.$id || item.id}`,
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
// GET /:collection/:id - Get single item
|
|
500
|
-
apiRoutes.get('/:collection/:id', async (c) => {
|
|
501
|
-
const collection = c.req.param('collection');
|
|
502
|
-
const id = c.req.param('id');
|
|
503
|
-
// Check if collection is valid
|
|
504
|
-
if (!isValidCollection(collection)) {
|
|
505
|
-
return c.json({ error: { code: 'NOT_FOUND', message: `Collection not found: ${collection}` } }, 404);
|
|
506
|
-
}
|
|
507
|
-
// Forward to DO for storage
|
|
508
|
-
const stub = getCollectionStub(c.env, collection);
|
|
509
|
-
const doResponse = await stub.fetch(new Request(`http://do/things/${id}`, {
|
|
510
|
-
method: 'GET',
|
|
511
|
-
}));
|
|
512
|
-
if (doResponse.status === 404) {
|
|
513
|
-
return c.json({ error: { code: 'NOT_FOUND', message: 'Item not found' } }, 404);
|
|
514
|
-
}
|
|
515
|
-
const item = await doResponse.json();
|
|
516
|
-
return c.json(item);
|
|
517
|
-
});
|
|
518
|
-
// PUT /:collection/:id - Update item
|
|
519
|
-
apiRoutes.put('/:collection/:id', async (c) => {
|
|
520
|
-
const collection = c.req.param('collection');
|
|
521
|
-
const id = c.req.param('id');
|
|
522
|
-
// Check if collection is valid
|
|
523
|
-
if (!isValidCollection(collection)) {
|
|
524
|
-
return c.json({ error: { code: 'NOT_FOUND', message: `Collection not found: ${collection}` } }, 404);
|
|
525
|
-
}
|
|
526
|
-
let body;
|
|
527
|
-
try {
|
|
528
|
-
body = await c.req.json();
|
|
529
|
-
}
|
|
530
|
-
catch {
|
|
531
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } }, 400);
|
|
532
|
-
}
|
|
533
|
-
// Forward to DO for storage
|
|
534
|
-
const stub = getCollectionStub(c.env, collection);
|
|
535
|
-
const doResponse = await stub.fetch(new Request(`http://do/things/${id}`, {
|
|
536
|
-
method: 'PUT',
|
|
537
|
-
headers: { 'Content-Type': 'application/json' },
|
|
538
|
-
body: JSON.stringify(body),
|
|
539
|
-
}));
|
|
540
|
-
if (doResponse.status === 404) {
|
|
541
|
-
return c.json({ error: { code: 'NOT_FOUND', message: 'Item not found' } }, 404);
|
|
542
|
-
}
|
|
543
|
-
const item = await doResponse.json();
|
|
544
|
-
return c.json(item);
|
|
545
|
-
});
|
|
546
|
-
// PATCH /:collection/:id - Partial update item
|
|
547
|
-
apiRoutes.patch('/:collection/:id', async (c) => {
|
|
548
|
-
const collection = c.req.param('collection');
|
|
549
|
-
const id = c.req.param('id');
|
|
550
|
-
// Check if collection is valid
|
|
551
|
-
if (!isValidCollection(collection)) {
|
|
552
|
-
return c.json({ error: { code: 'NOT_FOUND', message: `Collection not found: ${collection}` } }, 404);
|
|
553
|
-
}
|
|
554
|
-
// Check Content-Type - support both regular JSON and JSON Patch
|
|
555
|
-
const contentType = c.req.header('content-type') || '';
|
|
556
|
-
if (contentType.includes('application/json-patch+json')) {
|
|
557
|
-
// JSON Patch format - not implemented, return 415
|
|
558
|
-
return c.json({ error: { code: 'UNSUPPORTED_MEDIA_TYPE', message: 'JSON Patch not supported' } }, 415);
|
|
559
|
-
}
|
|
560
|
-
let body;
|
|
561
|
-
try {
|
|
562
|
-
body = await c.req.json();
|
|
563
|
-
}
|
|
564
|
-
catch {
|
|
565
|
-
return c.json({ error: { code: 'BAD_REQUEST', message: 'Invalid JSON body' } }, 400);
|
|
566
|
-
}
|
|
567
|
-
// Remove $id from body to prevent overwriting
|
|
568
|
-
delete body.$id;
|
|
569
|
-
// Get existing item first
|
|
570
|
-
const stub = getCollectionStub(c.env, collection);
|
|
571
|
-
const getResponse = await stub.fetch(new Request(`http://do/things/${id}`, {
|
|
572
|
-
method: 'GET',
|
|
573
|
-
}));
|
|
574
|
-
if (getResponse.status === 404) {
|
|
575
|
-
return c.json({ error: { code: 'NOT_FOUND', message: 'Item not found' } }, 404);
|
|
576
|
-
}
|
|
577
|
-
const existing = await getResponse.json();
|
|
578
|
-
// Merge and update
|
|
579
|
-
const merged = { ...existing, ...body };
|
|
580
|
-
delete merged.$id; // Ensure $id from body doesn't override
|
|
581
|
-
const doResponse = await stub.fetch(new Request(`http://do/things/${id}`, {
|
|
582
|
-
method: 'PUT',
|
|
583
|
-
headers: { 'Content-Type': 'application/json' },
|
|
584
|
-
body: JSON.stringify(merged),
|
|
585
|
-
}));
|
|
586
|
-
const item = await doResponse.json();
|
|
587
|
-
return c.json(item);
|
|
588
|
-
});
|
|
589
|
-
// DELETE /:collection/:id - Delete item
|
|
590
|
-
apiRoutes.delete('/:collection/:id', async (c) => {
|
|
591
|
-
const collection = c.req.param('collection');
|
|
592
|
-
const id = c.req.param('id');
|
|
593
|
-
// Check if collection is valid
|
|
594
|
-
if (!isValidCollection(collection)) {
|
|
595
|
-
return c.json({ error: { code: 'NOT_FOUND', message: `Collection not found: ${collection}` } }, 404);
|
|
596
|
-
}
|
|
597
|
-
// Forward to DO for storage
|
|
598
|
-
const stub = getCollectionStub(c.env, collection);
|
|
599
|
-
const doResponse = await stub.fetch(new Request(`http://do/things/${id}`, {
|
|
600
|
-
method: 'DELETE',
|
|
601
|
-
}));
|
|
602
|
-
if (doResponse.status === 404) {
|
|
603
|
-
return c.json({ error: { code: 'NOT_FOUND', message: 'Item not found' } }, 404);
|
|
604
|
-
}
|
|
605
|
-
return new Response(null, { status: 204 });
|
|
606
|
-
});
|
|
607
|
-
// Catch-all for unknown API routes
|
|
608
|
-
apiRoutes.all('*', (c) => {
|
|
609
|
-
return c.json({ error: { code: 'NOT_FOUND', message: `Not found: ${c.req.path}` } }, 404);
|
|
610
|
-
});
|
|
611
|
-
export default apiRoutes;
|
|
612
|
-
//# sourceMappingURL=api.js.map
|