@syncular/server-hono 0.0.6-95 → 0.2.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.
Files changed (104) hide show
  1. package/README.md +5 -23
  2. package/dist/admin-page.d.ts +14 -0
  3. package/dist/admin-page.js +217 -0
  4. package/dist/admin.d.ts +39 -0
  5. package/dist/admin.js +142 -0
  6. package/dist/index.d.ts +16 -14
  7. package/dist/index.js +134 -23
  8. package/package.json +19 -53
  9. package/src/admin-page.ts +217 -0
  10. package/src/admin.ts +193 -0
  11. package/src/index.ts +175 -31
  12. package/dist/api-key-auth.d.ts +0 -49
  13. package/dist/api-key-auth.d.ts.map +0 -1
  14. package/dist/api-key-auth.js +0 -110
  15. package/dist/api-key-auth.js.map +0 -1
  16. package/dist/blobs.d.ts +0 -69
  17. package/dist/blobs.d.ts.map +0 -1
  18. package/dist/blobs.js +0 -383
  19. package/dist/blobs.js.map +0 -1
  20. package/dist/console/gateway.d.ts +0 -33
  21. package/dist/console/gateway.d.ts.map +0 -1
  22. package/dist/console/gateway.js +0 -2534
  23. package/dist/console/gateway.js.map +0 -1
  24. package/dist/console/index.d.ts +0 -11
  25. package/dist/console/index.d.ts.map +0 -1
  26. package/dist/console/index.js +0 -11
  27. package/dist/console/index.js.map +0 -1
  28. package/dist/console/routes.d.ts +0 -33
  29. package/dist/console/routes.d.ts.map +0 -1
  30. package/dist/console/routes.js +0 -2890
  31. package/dist/console/routes.js.map +0 -1
  32. package/dist/console/schemas.d.ts +0 -490
  33. package/dist/console/schemas.d.ts.map +0 -1
  34. package/dist/console/schemas.js +0 -303
  35. package/dist/console/schemas.js.map +0 -1
  36. package/dist/console/types.d.ts +0 -142
  37. package/dist/console/types.d.ts.map +0 -1
  38. package/dist/console/types.js +0 -2
  39. package/dist/console/types.js.map +0 -1
  40. package/dist/console/ui.d.ts +0 -38
  41. package/dist/console/ui.d.ts.map +0 -1
  42. package/dist/console/ui.js +0 -43
  43. package/dist/console/ui.js.map +0 -1
  44. package/dist/create-server.d.ts +0 -68
  45. package/dist/create-server.d.ts.map +0 -1
  46. package/dist/create-server.js +0 -109
  47. package/dist/create-server.js.map +0 -1
  48. package/dist/index.d.ts.map +0 -1
  49. package/dist/index.js.map +0 -1
  50. package/dist/openapi.d.ts +0 -45
  51. package/dist/openapi.d.ts.map +0 -1
  52. package/dist/openapi.js +0 -59
  53. package/dist/openapi.js.map +0 -1
  54. package/dist/proxy/connection-manager.d.ts +0 -78
  55. package/dist/proxy/connection-manager.d.ts.map +0 -1
  56. package/dist/proxy/connection-manager.js +0 -251
  57. package/dist/proxy/connection-manager.js.map +0 -1
  58. package/dist/proxy/index.d.ts +0 -8
  59. package/dist/proxy/index.d.ts.map +0 -1
  60. package/dist/proxy/index.js +0 -8
  61. package/dist/proxy/index.js.map +0 -1
  62. package/dist/proxy/routes.d.ts +0 -74
  63. package/dist/proxy/routes.d.ts.map +0 -1
  64. package/dist/proxy/routes.js +0 -147
  65. package/dist/proxy/routes.js.map +0 -1
  66. package/dist/rate-limit.d.ts +0 -101
  67. package/dist/rate-limit.d.ts.map +0 -1
  68. package/dist/rate-limit.js +0 -186
  69. package/dist/rate-limit.js.map +0 -1
  70. package/dist/routes.d.ts +0 -145
  71. package/dist/routes.d.ts.map +0 -1
  72. package/dist/routes.js +0 -1471
  73. package/dist/routes.js.map +0 -1
  74. package/dist/ws.d.ts +0 -235
  75. package/dist/ws.d.ts.map +0 -1
  76. package/dist/ws.js +0 -614
  77. package/dist/ws.js.map +0 -1
  78. package/src/__tests__/blob-routes.test.ts +0 -424
  79. package/src/__tests__/console-gateway-live-routes.test.ts +0 -297
  80. package/src/__tests__/console-gateway-routes.test.ts +0 -1364
  81. package/src/__tests__/console-routes.test.ts +0 -1543
  82. package/src/__tests__/console-ui.test.ts +0 -114
  83. package/src/__tests__/create-server.test.ts +0 -342
  84. package/src/__tests__/pull-chunk-storage.test.ts +0 -576
  85. package/src/__tests__/rate-limit.test.ts +0 -78
  86. package/src/__tests__/realtime-bridge.test.ts +0 -135
  87. package/src/__tests__/sync-rate-limit-routing.test.ts +0 -185
  88. package/src/__tests__/ws-connection-manager.test.ts +0 -176
  89. package/src/api-key-auth.ts +0 -179
  90. package/src/blobs.ts +0 -534
  91. package/src/console/gateway.ts +0 -3603
  92. package/src/console/index.ts +0 -11
  93. package/src/console/routes.ts +0 -3748
  94. package/src/console/schemas.ts +0 -434
  95. package/src/console/types.ts +0 -151
  96. package/src/console/ui.ts +0 -100
  97. package/src/create-server.ts +0 -206
  98. package/src/openapi.ts +0 -74
  99. package/src/proxy/connection-manager.ts +0 -340
  100. package/src/proxy/index.ts +0 -8
  101. package/src/proxy/routes.ts +0 -223
  102. package/src/rate-limit.ts +0 -321
  103. package/src/routes.ts +0 -2007
  104. package/src/ws.ts +0 -802
package/src/index.ts CHANGED
@@ -1,42 +1,186 @@
1
1
  /**
2
- * @syncular/server-hono - Hono adapter for sync infrastructure
3
- *
4
- * This package provides Hono-specific routes for @syncular/server.
5
- * Keeps @syncular/server framework-agnostic.
2
+ * Hono adapter (REVISE B2): a thin wrapper proving the embed boundary.
3
+ * Hono is a dependency of this adapter only, never of the server core.
4
+ * Mounts the §1.1 routes: POST /sync and GET /segments/:segmentId
5
+ * (realtime upgrades are runtime-specific and stay with the host).
6
6
  */
7
+ import {
8
+ encodeSegmentBody,
9
+ errorBody,
10
+ handleBlobDownload,
11
+ handleBlobUpload,
12
+ handleBlobUploadGrant,
13
+ handleSegmentDownload,
14
+ handleSyncRequest,
15
+ SSP2_CONTENT_TYPE,
16
+ SyncError,
17
+ type SyncServerConfig,
18
+ } from '@syncular/server';
19
+ import { Hono } from 'hono';
7
20
 
8
- // API Key Auth
9
- export * from './api-key-auth';
21
+ export * from './admin';
10
22
 
11
- // Blob routes
12
- export { type CreateBlobRoutesOptions, createBlobRoutes } from './blobs';
23
+ export interface SyncularHonoOptions {
24
+ readonly config: SyncServerConfig;
25
+ /** Host authentication (§1.1); `null` ⇒ 401 `sync.auth_required`. */
26
+ readonly authenticate: (
27
+ request: Request,
28
+ ) => Promise<{ actorId: string; partition: string } | null>;
29
+ }
13
30
 
14
- // Console
15
- export * from './console';
31
+ function errorResponse(error: unknown): Response {
32
+ const sync =
33
+ error instanceof SyncError
34
+ ? error
35
+ : new SyncError('sync.invalid_request', String(error));
36
+ return Response.json(errorBody(sync), { status: sync.httpStatus });
37
+ }
16
38
 
17
- // Simplified server factory
18
- export {
19
- createSyncServer,
20
- type SyncServerOptions,
21
- type SyncServerResult,
22
- } from './create-server';
39
+ export function createSyncularHono(options: SyncularHonoOptions): Hono {
40
+ const app = new Hono();
23
41
 
24
- // OpenAPI utilities
25
- export * from './openapi';
42
+ app.post('/sync', async (c) => {
43
+ const contentType = c.req.header('content-type')?.split(';')[0]?.trim();
44
+ if (contentType !== SSP2_CONTENT_TYPE) {
45
+ // §1.1: any other content type is rejected with HTTP 415.
46
+ return Response.json(
47
+ errorBody(
48
+ new SyncError('sync.invalid_request', 'unsupported content type'),
49
+ ),
50
+ { status: 415 },
51
+ );
52
+ }
53
+ const auth = await options.authenticate(c.req.raw);
54
+ if (auth === null)
55
+ return errorResponse(new SyncError('sync.auth_required'));
56
+ try {
57
+ const bytes = new Uint8Array(await c.req.arrayBuffer());
58
+ const out = await handleSyncRequest(bytes, {
59
+ ...options.config,
60
+ ...auth,
61
+ });
62
+ return c.body(out.slice().buffer as ArrayBuffer, 200, {
63
+ 'Content-Type': SSP2_CONTENT_TYPE,
64
+ });
65
+ } catch (error) {
66
+ return errorResponse(error);
67
+ }
68
+ });
26
69
 
27
- // Proxy
28
- export * from './proxy';
70
+ app.get('/segments/:segmentId', async (c) => {
71
+ const auth = await options.authenticate(c.req.raw);
72
+ if (auth === null)
73
+ return errorResponse(new SyncError('sync.auth_required'));
74
+ try {
75
+ const result = await handleSegmentDownload(
76
+ { ...options.config, ...auth },
77
+ {
78
+ segmentId: c.req.param('segmentId'),
79
+ scopesHeader: c.req.header('x-syncular-scopes') ?? '{}',
80
+ },
81
+ );
82
+ if (c.req.header('if-none-match') === result.headers.ETag) {
83
+ return c.body(null, 304, result.headers);
84
+ }
85
+ // §5.8 shipped default: compress the body per Accept-Encoding
86
+ // (zstd preferred, gzip fallback, identity otherwise). Content
87
+ // addresses are over the uncompressed bytes (§5.1) — fetch
88
+ // decodes transparently on the client.
89
+ const encoded = encodeSegmentBody(
90
+ result.bytes,
91
+ c.req.header('accept-encoding'),
92
+ );
93
+ return c.body(encoded.bytes.slice().buffer as ArrayBuffer, 200, {
94
+ ...result.headers,
95
+ ...(encoded.contentEncoding !== undefined
96
+ ? { 'Content-Encoding': encoded.contentEncoding }
97
+ : {}),
98
+ });
99
+ } catch (error) {
100
+ return errorResponse(error);
101
+ }
102
+ });
29
103
 
30
- // Rate limiting
31
- export * from './rate-limit';
104
+ // §5.9.3: blob upload with server-side content-address verification.
105
+ app.put('/blobs/:blobId', async (c) => {
106
+ const auth = await options.authenticate(c.req.raw);
107
+ if (auth === null)
108
+ return errorResponse(new SyncError('sync.auth_required'));
109
+ try {
110
+ const bytes = new Uint8Array(await c.req.arrayBuffer());
111
+ const contentType = c.req.header('content-type')?.split(';')[0]?.trim();
112
+ await handleBlobUpload(
113
+ { ...options.config, ...auth },
114
+ {
115
+ blobId: c.req.param('blobId'),
116
+ bytes,
117
+ ...(contentType !== undefined &&
118
+ contentType !== 'application/octet-stream'
119
+ ? { mediaType: contentType }
120
+ : {}),
121
+ },
122
+ );
123
+ return c.body(null, 200);
124
+ } catch (error) {
125
+ return errorResponse(error);
126
+ }
127
+ });
32
128
 
33
- // Route types and factory
34
- export {
35
- type CreateSyncRoutesOptions,
36
- createSyncRoutes,
37
- getSyncRealtimeUnsubscribe,
38
- getSyncWebSocketConnectionManager,
39
- } from './routes';
129
+ // §5.9.3: presigned-upload grant — mint a direct-to-storage PUT URL.
130
+ app.post('/blobs/:blobId/upload-grant', async (c) => {
131
+ const auth = await options.authenticate(c.req.raw);
132
+ if (auth === null)
133
+ return errorResponse(new SyncError('sync.auth_required'));
134
+ try {
135
+ const body = (await c.req.json().catch(() => ({}))) as {
136
+ byteLength?: number;
137
+ mediaType?: string;
138
+ };
139
+ const grant = await handleBlobUploadGrant(
140
+ { ...options.config, ...auth },
141
+ {
142
+ blobId: c.req.param('blobId'),
143
+ byteLength: Number(body.byteLength ?? 0),
144
+ ...(typeof body.mediaType === 'string'
145
+ ? { mediaType: body.mediaType }
146
+ : {}),
147
+ },
148
+ );
149
+ return c.json(grant, 200);
150
+ } catch (error) {
151
+ return errorResponse(error);
152
+ }
153
+ });
40
154
 
41
- // WebSocket helpers for realtime sync
42
- export * from './ws';
155
+ // §5.9.5: blob download, re-authorized against referencing rows. When the
156
+ // host configured presigned URLs, the result carries `url` (no bytes) and
157
+ // the client fetches it directly (§5.9.5 always-issue).
158
+ app.get('/blobs/:blobId', async (c) => {
159
+ const auth = await options.authenticate(c.req.raw);
160
+ if (auth === null)
161
+ return errorResponse(new SyncError('sync.auth_required'));
162
+ try {
163
+ const result = await handleBlobDownload(
164
+ { ...options.config, ...auth },
165
+ c.req.param('blobId'),
166
+ );
167
+ if (result.url !== undefined) {
168
+ return c.json(
169
+ { url: result.url, urlExpiresAtMs: result.urlExpiresAtMs },
170
+ 200,
171
+ );
172
+ }
173
+ if (c.req.header('if-none-match') === result.headers.ETag) {
174
+ return c.body(null, 304, result.headers);
175
+ }
176
+ const bytes = result.bytes ?? new Uint8Array(0);
177
+ return c.body(bytes.slice().buffer as ArrayBuffer, 200, {
178
+ ...result.headers,
179
+ });
180
+ } catch (error) {
181
+ return errorResponse(error);
182
+ }
183
+ });
184
+
185
+ return app;
186
+ }
@@ -1,49 +0,0 @@
1
- /**
2
- * @syncular/server-hono - API Key Authentication Helper
3
- *
4
- * Provides utilities for validating API keys in relay/proxy routes.
5
- */
6
- import type { ServerSyncDialect } from '@syncular/server';
7
- import type { Context } from 'hono';
8
- import { type Kysely } from 'kysely';
9
- import { type ApiKeyType } from './console/schemas';
10
- interface SyncApiKeysTable {
11
- key_id: string;
12
- key_hash: string;
13
- key_prefix: string;
14
- name: string;
15
- key_type: string;
16
- scope_keys: unknown | null;
17
- actor_id: string | null;
18
- created_at: string;
19
- expires_at: string | null;
20
- last_used_at: string | null;
21
- revoked_at: string | null;
22
- }
23
- type ApiKeyDb = {
24
- sync_api_keys: SyncApiKeysTable;
25
- };
26
- interface ValidateApiKeyResult {
27
- keyId: string;
28
- keyType: ApiKeyType;
29
- actorId: string | null;
30
- scopeKeys: string[];
31
- }
32
- /**
33
- * Validates an API key from Authorization header.
34
- * Updates last_used_at on successful validation.
35
- */
36
- export declare function validateApiKey<DB extends ApiKeyDb>(db: Kysely<DB>, dialect: ServerSyncDialect, authHeader: string | undefined): Promise<ValidateApiKeyResult | null>;
37
- /**
38
- * Creates an authenticator for relay/proxy routes.
39
- * Returns actorId from the API key if valid and allowed.
40
- */
41
- export declare function createApiKeyAuthenticator<DB extends ApiKeyDb>(db: Kysely<DB>, dialect: ServerSyncDialect, allowedTypes: ApiKeyType[]): (c: Context) => Promise<{
42
- actorId: string;
43
- } | null>;
44
- /**
45
- * Middleware that validates API key and attaches result to context.
46
- */
47
- export declare function apiKeyAuthMiddleware<DB extends ApiKeyDb>(db: Kysely<DB>, dialect: ServerSyncDialect, allowedTypes: ApiKeyType[]): (c: Context<any, any, {}>, next: () => Promise<void>) => Promise<Response | undefined>;
48
- export {};
49
- //# sourceMappingURL=api-key-auth.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api-key-auth.d.ts","sourceRoot":"","sources":["../src/api-key-auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,KAAK,MAAM,EAAO,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AAEtE,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,KAAK,QAAQ,GAAG;IACd,aAAa,EAAE,gBAAgB,CAAC;CACjC,CAAC;AAEF,UAAU,oBAAoB;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,EAAE,SAAS,QAAQ,EACtD,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAkEtC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,SAAS,QAAQ,EAC3D,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,OAAO,EAAE,iBAAiB,EAC1B,YAAY,EAAE,UAAU,EAAE,GACzB,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAmBrD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,QAAQ,EACtD,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,OAAO,EAAE,iBAAiB,EAC1B,YAAY,EAAE,UAAU,EAAE,0FAsB3B"}
@@ -1,110 +0,0 @@
1
- /**
2
- * @syncular/server-hono - API Key Authentication Helper
3
- *
4
- * Provides utilities for validating API keys in relay/proxy routes.
5
- */
6
- import { sql } from 'kysely';
7
- import { ApiKeyTypeSchema } from './console/schemas.js';
8
- /**
9
- * Validates an API key from Authorization header.
10
- * Updates last_used_at on successful validation.
11
- */
12
- export async function validateApiKey(db, dialect, authHeader) {
13
- if (!authHeader?.startsWith('Bearer ')) {
14
- return null;
15
- }
16
- const secretKey = authHeader.slice(7);
17
- if (!secretKey || !secretKey.startsWith('sk_')) {
18
- return null;
19
- }
20
- // Hash the provided key
21
- const keyHash = await hashApiKey(secretKey);
22
- // Look up key by hash
23
- const rowResult = await sql `
24
- select key_id, key_type, actor_id, scope_keys, expires_at, revoked_at
25
- from ${sql.table('sync_api_keys')}
26
- where key_hash = ${keyHash}
27
- limit 1
28
- `.execute(db);
29
- const row = rowResult.rows[0];
30
- if (!row) {
31
- return null;
32
- }
33
- const parsedKeyType = ApiKeyTypeSchema.safeParse(row.key_type);
34
- if (!parsedKeyType.success)
35
- return null;
36
- // Check if revoked
37
- if (row.revoked_at) {
38
- return null;
39
- }
40
- // Check if expired
41
- if (row.expires_at) {
42
- const expiresAt = new Date(row.expires_at);
43
- if (expiresAt < new Date()) {
44
- return null;
45
- }
46
- }
47
- // Update last_used_at
48
- const now = new Date().toISOString();
49
- await sql `
50
- update ${sql.table('sync_api_keys')}
51
- set last_used_at = ${now}
52
- where key_id = ${row.key_id}
53
- `.execute(db);
54
- // Parse scopes
55
- const scopeKeys = dialect.dbToArray(row.scope_keys);
56
- return {
57
- keyId: row.key_id,
58
- keyType: parsedKeyType.data,
59
- actorId: row.actor_id,
60
- scopeKeys,
61
- };
62
- }
63
- /**
64
- * Creates an authenticator for relay/proxy routes.
65
- * Returns actorId from the API key if valid and allowed.
66
- */
67
- export function createApiKeyAuthenticator(db, dialect, allowedTypes) {
68
- return async (c) => {
69
- const authHeader = c.req.header('Authorization');
70
- const result = await validateApiKey(db, dialect, authHeader);
71
- if (!result) {
72
- return null;
73
- }
74
- // Check if key type is allowed
75
- if (!allowedTypes.includes(result.keyType)) {
76
- return null;
77
- }
78
- // Return actorId (use key's actorId if set, otherwise use a default)
79
- return {
80
- actorId: result.actorId ?? `api-key:${result.keyId}`,
81
- };
82
- };
83
- }
84
- /**
85
- * Middleware that validates API key and attaches result to context.
86
- */
87
- export function apiKeyAuthMiddleware(db, dialect, allowedTypes) {
88
- return async (c, next) => {
89
- const authHeader = c.req.header('Authorization');
90
- const result = await validateApiKey(db, dialect, authHeader);
91
- if (!result) {
92
- return c.json({ error: 'UNAUTHENTICATED' }, 401);
93
- }
94
- if (!allowedTypes.includes(result.keyType)) {
95
- return c.json({ error: 'FORBIDDEN', message: 'Invalid key type' }, 403);
96
- }
97
- // Attach to context for downstream handlers
98
- c.set('apiKey', result);
99
- await next();
100
- };
101
- }
102
- // Hash function (same as in routes.ts)
103
- async function hashApiKey(secretKey) {
104
- const encoder = new TextEncoder();
105
- const data = encoder.encode(secretKey);
106
- const hashBuffer = await crypto.subtle.digest('SHA-256', data);
107
- const hashArray = new Uint8Array(hashBuffer);
108
- return Array.from(hashArray, (b) => b.toString(16).padStart(2, '0')).join('');
109
- }
110
- //# sourceMappingURL=api-key-auth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api-key-auth.js","sourceRoot":"","sources":["../src/api-key-auth.ts"],"names":[],"mappings":"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"}
package/dist/blobs.d.ts DELETED
@@ -1,69 +0,0 @@
1
- /**
2
- * @syncular/server-hono - Blob routes for media/binary handling
3
- *
4
- * Provides:
5
- * - POST /blobs/upload - Initiate a blob upload (get presigned URL)
6
- * - POST /blobs/:hash/complete - Complete a blob upload
7
- * - GET /blobs/:hash/url - Get a presigned download URL
8
- * - PUT /blobs/:hash/upload - Direct upload (for database adapter)
9
- * - GET /blobs/:hash/download - Direct download (for database adapter)
10
- */
11
- import type { BlobManager } from '@syncular/server';
12
- import { type BlobTokenSigner, type SyncBlobsDb } from '@syncular/server';
13
- import type { Context } from 'hono';
14
- import { Hono } from 'hono';
15
- import type { Kysely } from 'kysely';
16
- interface BlobAuthResult {
17
- actorId: string;
18
- }
19
- export interface CreateBlobRoutesOptions<DB extends SyncBlobsDb = SyncBlobsDb> {
20
- /** Blob manager instance */
21
- blobManager: BlobManager;
22
- /** Authentication function */
23
- authenticate: (c: Context) => Promise<BlobAuthResult | null>;
24
- /**
25
- * Token signer for database adapter direct uploads/downloads.
26
- * Required if using the database blob storage adapter.
27
- */
28
- tokenSigner?: BlobTokenSigner;
29
- /**
30
- * Database instance for direct blob storage.
31
- * Required if using the database blob storage adapter.
32
- */
33
- db?: Kysely<DB>;
34
- /**
35
- * Optional: Check if actor can access a blob.
36
- * By default, any authenticated actor can access any completed blob.
37
- * Provide this to implement scope-based access control.
38
- */
39
- canAccessBlob?: (args: {
40
- actorId: string;
41
- hash: string;
42
- }) => Promise<boolean>;
43
- /**
44
- * Maximum upload size in bytes.
45
- * Default: 100MB (104857600)
46
- */
47
- maxUploadSize?: number;
48
- }
49
- /**
50
- * Create blob routes for Hono.
51
- *
52
- * @example
53
- * ```typescript
54
- * const blobRoutes = createBlobRoutes({
55
- * blobManager,
56
- * authenticate: async (c) => {
57
- * const token = c.req.header('Authorization')?.replace('Bearer ', '');
58
- * if (!token) return null;
59
- * const user = await verifyToken(token);
60
- * return user ? { actorId: user.id } : null;
61
- * },
62
- * });
63
- *
64
- * app.route('/api/sync', blobRoutes);
65
- * ```
66
- */
67
- export declare function createBlobRoutes<DB extends SyncBlobsDb>(options: CreateBlobRoutesOptions<DB>): Hono;
68
- export {};
69
- //# sourceMappingURL=blobs.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blobs.d.ts","sourceRoot":"","sources":["../src/blobs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,OAAO,KAAK,EACV,WAAW,EAGZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,WAAW,EAEjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB,CAAC,EAAE,SAAS,WAAW,GAAG,WAAW;IAC3E,4BAA4B;IAC5B,WAAW,EAAE,WAAW,CAAC;IACzB,8BAA8B;IAC9B,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC7D;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IAChB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9E;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAUD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,WAAW,EACrD,OAAO,EAAE,uBAAuB,CAAC,EAAE,CAAC,GACnC,IAAI,CAwZN"}