@alteran/astro 0.1.3 → 0.1.5
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 +24 -3
- package/index.js +1 -12
- package/package.json +5 -1
- package/src/_worker.ts +3 -40
- package/src/pages/debug/db/commits.ts +1 -1
- package/src/pages/debug/gc/blobs.ts +1 -1
- package/src/pages/debug/record.ts +1 -1
- package/src/pages/xrpc/com.atproto.identity.updateHandle.ts +1 -1
- package/src/pages/xrpc/com.atproto.repo.applyWrites.ts +3 -3
- package/src/pages/xrpc/com.atproto.repo.createRecord.ts +5 -5
- package/src/pages/xrpc/com.atproto.repo.deleteRecord.ts +5 -5
- package/src/pages/xrpc/com.atproto.repo.describeRepo.ts +1 -1
- package/src/pages/xrpc/com.atproto.repo.getRecord.ts +1 -1
- package/src/pages/xrpc/com.atproto.repo.listRecords.ts +1 -1
- package/src/pages/xrpc/com.atproto.repo.putRecord.ts +5 -5
- package/src/pages/xrpc/com.atproto.repo.uploadBlob.ts +5 -5
- package/src/pages/xrpc/com.atproto.server.createSession.ts +3 -3
- package/src/pages/xrpc/com.atproto.server.refreshSession.ts +4 -4
- package/src/pages/xrpc/com.atproto.sync.getBlocks.json.ts +2 -2
- package/src/pages/xrpc/com.atproto.sync.getBlocks.ts +3 -3
- package/src/pages/xrpc/com.atproto.sync.getCheckout.json.ts +3 -3
- package/src/pages/xrpc/com.atproto.sync.getCheckout.ts +1 -1
- package/src/pages/xrpc/com.atproto.sync.getHead.ts +1 -1
- package/src/pages/xrpc/com.atproto.sync.getLatestCommit.ts +1 -1
- package/src/pages/xrpc/com.atproto.sync.getRecord.ts +2 -2
- package/src/pages/xrpc/com.atproto.sync.getRepo.json.ts +3 -3
- package/src/pages/xrpc/com.atproto.sync.getRepo.range.ts +1 -1
- package/src/pages/xrpc/com.atproto.sync.getRepo.ts +1 -1
- package/src/pages/xrpc/com.atproto.sync.listBlobs.ts +1 -1
- package/src/worker/index.ts +6 -0
- package/src/worker/runtime.ts +63 -0
- package/types/worker.d.ts +5 -0
package/README.md
CHANGED
|
@@ -27,19 +27,40 @@ By default the integration injects all `/xrpc/*` ATProto routes, health/ready ch
|
|
|
27
27
|
alteran({
|
|
28
28
|
debugRoutes: process.env.NODE_ENV !== 'production',
|
|
29
29
|
includeRootEndpoint: false,
|
|
30
|
-
injectServerEntry: true,
|
|
30
|
+
injectServerEntry: true, // opt in if you don't maintain your own worker entrypoint
|
|
31
31
|
});
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
The integration automatically:
|
|
35
|
-
-
|
|
35
|
+
- Resolves all injected routes against the packaged runtime without requiring a Vite alias
|
|
36
36
|
- Registers the middleware that applies structured logging and CORS enforcement
|
|
37
37
|
- Injects all PDS HTTP endpoints into the host project
|
|
38
|
-
-
|
|
38
|
+
- Offers the packaged Cloudflare worker entrypoint when you enable `{ injectServerEntry: true }`
|
|
39
39
|
- Publishes ambient env typings so `Env` and `App.Locals` are available from TypeScript
|
|
40
40
|
|
|
41
41
|
When deploying, continue to configure Wrangler/D1/R2 secrets exactly as before—the integration does not change the runtime requirements.
|
|
42
42
|
|
|
43
|
+
### Custom Worker Entrypoint
|
|
44
|
+
|
|
45
|
+
The integration no longer overrides `build.serverEntry` by default. If you need to export additional Durable Objects or otherwise customise the worker, keep your own entrypoint and compose Alteran's runtime helpers instead of copying the internal logic.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// src/_worker.ts in your Astro project
|
|
49
|
+
import { createPdsFetchHandler, Sequencer } from '@alteran/astro/worker';
|
|
50
|
+
|
|
51
|
+
const fetch = createPdsFetchHandler();
|
|
52
|
+
|
|
53
|
+
export default { fetch };
|
|
54
|
+
|
|
55
|
+
// Re-export Sequencer so Wrangler can bind the Durable Object namespace
|
|
56
|
+
export { Sequencer };
|
|
57
|
+
|
|
58
|
+
// Export any additional Durable Objects after this line
|
|
59
|
+
export { MyDurableObject } from './worker/my-durable-object';
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Helpers like `onRequest`, `seed`, and `validateConfigOrThrow` are also exported from `@alteran/astro/worker` if you need to build more advanced wrappers (for example, to add request instrumentation before delegating to the PDS handler).
|
|
63
|
+
|
|
43
64
|
To install dependencies:
|
|
44
65
|
|
|
45
66
|
```bash
|
package/index.js
CHANGED
|
@@ -56,10 +56,9 @@ export default function alteran(options = {}) {
|
|
|
56
56
|
const {
|
|
57
57
|
debugRoutes = false,
|
|
58
58
|
includeRootEndpoint = false,
|
|
59
|
-
injectServerEntry =
|
|
59
|
+
injectServerEntry = false,
|
|
60
60
|
} = options;
|
|
61
61
|
|
|
62
|
-
const aliasTarget = resolvePackagePath('./src');
|
|
63
62
|
const middlewareEntrypoint = resolvePackagePath('./src/middleware.ts');
|
|
64
63
|
const serverEntrypoint = resolvePackagePath('./src/_worker.ts');
|
|
65
64
|
|
|
@@ -79,16 +78,6 @@ export default function alteran(options = {}) {
|
|
|
79
78
|
updateConfig({ output: 'server' });
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
updateConfig({
|
|
83
|
-
vite: {
|
|
84
|
-
resolve: {
|
|
85
|
-
alias: {
|
|
86
|
-
'@alteran': aliasTarget,
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
|
|
92
81
|
if (injectServerEntry) {
|
|
93
82
|
if (config.build?.serverEntry && config.build.serverEntry !== serverEntrypoint) {
|
|
94
83
|
logger.info(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alteran/astro",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Astro integration for running a Cloudflare-hosted Bluesky PDS with Alteran.",
|
|
5
5
|
"module": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./index.d.ts",
|
|
10
10
|
"import": "./index.js"
|
|
11
|
+
},
|
|
12
|
+
"./worker": {
|
|
13
|
+
"types": "./types/worker.d.ts",
|
|
14
|
+
"import": "./src/worker/index.ts"
|
|
11
15
|
}
|
|
12
16
|
},
|
|
13
17
|
"type": "module",
|
package/src/_worker.ts
CHANGED
|
@@ -1,44 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { onRequest } from './middleware';
|
|
3
|
-
import { seed } from './db/seed';
|
|
4
|
-
import { validateConfigOrThrow } from './lib/config';
|
|
5
|
-
import type { Env } from './env';
|
|
1
|
+
import { createPdsFetchHandler } from './worker/runtime';
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
|
9
|
-
// Validate configuration on startup (fail fast if invalid)
|
|
10
|
-
try {
|
|
11
|
-
validateConfigOrThrow(env);
|
|
12
|
-
} catch (error) {
|
|
13
|
-
return new Response(
|
|
14
|
-
JSON.stringify({
|
|
15
|
-
error: 'ConfigurationError',
|
|
16
|
-
message: error instanceof Error ? error.message : 'Invalid configuration',
|
|
17
|
-
}),
|
|
18
|
-
{
|
|
19
|
-
status: 500,
|
|
20
|
-
headers: { 'Content-Type': 'application/json' },
|
|
21
|
-
}
|
|
22
|
-
);
|
|
23
|
-
}
|
|
3
|
+
const fetch = createPdsFetchHandler();
|
|
24
4
|
|
|
25
|
-
|
|
5
|
+
export default { fetch };
|
|
26
6
|
|
|
27
|
-
const url = new URL(request.url);
|
|
28
|
-
if (url.pathname === '/xrpc/com.atproto.sync.subscribeRepos') {
|
|
29
|
-
const upgrade = request.headers.get('upgrade');
|
|
30
|
-
if (upgrade !== 'websocket') return new Response('Expected websocket', { status: 426 });
|
|
31
|
-
if (!env.SEQUENCER) return new Response('Sequencer not configured', { status: 503 });
|
|
32
|
-
|
|
33
|
-
const id = env.SEQUENCER.idFromName('default');
|
|
34
|
-
const stub = env.SEQUENCER.get(id);
|
|
35
|
-
return stub.fetch(request as any);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const locals: any = { runtime: { env, ctx, request } };
|
|
39
|
-
return await onRequest(locals as any, async () => await handle(locals as any));
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// Export Durable Object(s)
|
|
44
7
|
export { Sequencer } from './worker/sequencer';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { RepoManager } from '
|
|
3
|
-
import { readJson } from '
|
|
4
|
-
import { bumpRoot } from '
|
|
2
|
+
import { RepoManager } from '../../services/repo-manager';
|
|
3
|
+
import { readJson } from '../../lib/util';
|
|
4
|
+
import { bumpRoot } from '../../db/repo';
|
|
5
5
|
|
|
6
6
|
export const prerender = false;
|
|
7
7
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { isAuthorized, unauthorized } from '
|
|
3
|
-
import { checkRate } from '
|
|
4
|
-
import { readJsonBounded } from '
|
|
5
|
-
import { RepoManager } from '
|
|
6
|
-
import { notifySequencer } from '
|
|
2
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
3
|
+
import { checkRate } from '../../lib/ratelimit';
|
|
4
|
+
import { readJsonBounded } from '../../lib/util';
|
|
5
|
+
import { RepoManager } from '../../services/repo-manager';
|
|
6
|
+
import { notifySequencer } from '../../lib/sequencer';
|
|
7
7
|
|
|
8
8
|
export const prerender = false;
|
|
9
9
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { isAuthorized, unauthorized } from '
|
|
3
|
-
import { checkRate } from '
|
|
4
|
-
import { readJsonBounded } from '
|
|
5
|
-
import { RepoManager } from '
|
|
6
|
-
import { notifySequencer } from '
|
|
2
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
3
|
+
import { checkRate } from '../../lib/ratelimit';
|
|
4
|
+
import { readJsonBounded } from '../../lib/util';
|
|
5
|
+
import { RepoManager } from '../../services/repo-manager';
|
|
6
|
+
import { notifySequencer } from '../../lib/sequencer';
|
|
7
7
|
|
|
8
8
|
export const prerender = false;
|
|
9
9
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { isAuthorized, unauthorized } from '
|
|
3
|
-
import { checkRate } from '
|
|
4
|
-
import { readJsonBounded } from '
|
|
5
|
-
import { RepoManager } from '
|
|
6
|
-
import { notifySequencer } from '
|
|
2
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
3
|
+
import { checkRate } from '../../lib/ratelimit';
|
|
4
|
+
import { readJsonBounded } from '../../lib/util';
|
|
5
|
+
import { RepoManager } from '../../services/repo-manager';
|
|
6
|
+
import { notifySequencer } from '../../lib/sequencer';
|
|
7
7
|
|
|
8
8
|
export const prerender = false;
|
|
9
9
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { isAuthorized, unauthorized } from '
|
|
3
|
-
import { checkRate } from '
|
|
4
|
-
import { isAllowedMime } from '
|
|
5
|
-
import { R2BlobStore } from '
|
|
6
|
-
import { putBlobRef, checkBlobQuota, updateBlobQuota } from '
|
|
2
|
+
import { isAuthorized, unauthorized } from '../../lib/auth';
|
|
3
|
+
import { checkRate } from '../../lib/ratelimit';
|
|
4
|
+
import { isAllowedMime } from '../../lib/util';
|
|
5
|
+
import { R2BlobStore } from '../../services/r2-blob-store';
|
|
6
|
+
import { putBlobRef, checkBlobQuota, updateBlobQuota } from '../../db/dal';
|
|
7
7
|
|
|
8
8
|
export const prerender = false;
|
|
9
9
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { signJwt } from '
|
|
3
|
-
import { readJson } from '
|
|
2
|
+
import { signJwt } from '../../lib/jwt';
|
|
3
|
+
import { readJson } from '../../lib/util';
|
|
4
4
|
import { drizzle } from 'drizzle-orm/d1';
|
|
5
|
-
import { login_attempts } from '
|
|
5
|
+
import { login_attempts } from '../../db/schema';
|
|
6
6
|
import { eq } from 'drizzle-orm';
|
|
7
7
|
|
|
8
8
|
export const prerender = false;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { signJwt, verifyJwt } from '
|
|
3
|
-
import { bearerToken } from '
|
|
4
|
-
import { lazyCleanupExpiredTokens } from '
|
|
2
|
+
import { signJwt, verifyJwt } from '../../lib/jwt';
|
|
3
|
+
import { bearerToken } from '../../lib/util';
|
|
4
|
+
import { lazyCleanupExpiredTokens } from '../../lib/token-cleanup';
|
|
5
5
|
import { drizzle } from 'drizzle-orm/d1';
|
|
6
|
-
import { token_revocation } from '
|
|
6
|
+
import { token_revocation } from '../../db/schema';
|
|
7
7
|
import { eq } from 'drizzle-orm';
|
|
8
8
|
|
|
9
9
|
export const prerender = false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { getRecordsByCids as dalGetByCids } from '
|
|
3
|
-
import { tryParse } from '
|
|
2
|
+
import { getRecordsByCids as dalGetByCids } from '../../db/dal';
|
|
3
|
+
import { tryParse } from '../../lib/util';
|
|
4
4
|
|
|
5
5
|
export const prerender = false;
|
|
6
6
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { NotFound } from '
|
|
3
|
-
import { D1Blockstore } from '
|
|
2
|
+
import { NotFound } from '../../lib/errors';
|
|
3
|
+
import { D1Blockstore } from '../../lib/mst';
|
|
4
4
|
import { CID } from 'multiformats/cid';
|
|
5
|
-
import { encodeExistingBlocksToCAR } from '
|
|
5
|
+
import { encodeExistingBlocksToCAR } from '../../services/car';
|
|
6
6
|
|
|
7
7
|
export const prerender = false;
|
|
8
8
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { getRoot as getRepoRoot } from '
|
|
3
|
-
import { listRecords as dalListRecords } from '
|
|
4
|
-
import { tryParse } from '
|
|
2
|
+
import { getRoot as getRepoRoot } from '../../db/repo';
|
|
3
|
+
import { listRecords as dalListRecords } from '../../db/dal';
|
|
4
|
+
import { tryParse } from '../../lib/util';
|
|
5
5
|
|
|
6
6
|
export const prerender = false;
|
|
7
7
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { RepoManager } from '
|
|
3
|
-
import { encodeRecordBlock } from '
|
|
2
|
+
import { RepoManager } from '../../services/repo-manager';
|
|
3
|
+
import { encodeRecordBlock } from '../../services/car';
|
|
4
4
|
import * as dagCbor from '@ipld/dag-cbor';
|
|
5
5
|
import { CID } from 'multiformats/cid';
|
|
6
6
|
import { sha256 } from 'multiformats/hashes/sha2';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { APIContext } from 'astro';
|
|
2
|
-
import { getRoot as getRepoRoot } from '
|
|
3
|
-
import { listRecords as dalListRecords } from '
|
|
4
|
-
import { tryParse } from '
|
|
2
|
+
import { getRoot as getRepoRoot } from '../../db/repo';
|
|
3
|
+
import { listRecords as dalListRecords } from '../../db/dal';
|
|
4
|
+
import { tryParse } from '../../lib/util';
|
|
5
5
|
|
|
6
6
|
export const prerender = false;
|
|
7
7
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createPdsFetchHandler } from './runtime';
|
|
2
|
+
export type { PdsFetchHandler } from './runtime';
|
|
3
|
+
export { Sequencer } from './sequencer';
|
|
4
|
+
export { onRequest } from '../middleware';
|
|
5
|
+
export { seed } from '../db/seed';
|
|
6
|
+
export { validateConfigOrThrow } from '../lib/config';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { handle } from 'astro/internal/handler';
|
|
2
|
+
import { onRequest } from '../middleware';
|
|
3
|
+
import { seed } from '../db/seed';
|
|
4
|
+
import { validateConfigOrThrow } from '../lib/config';
|
|
5
|
+
import type { Env } from '../env';
|
|
6
|
+
import type {
|
|
7
|
+
ExecutionContext,
|
|
8
|
+
Request as WorkersRequest,
|
|
9
|
+
Response as WorkersResponse,
|
|
10
|
+
} from '@cloudflare/workers-types';
|
|
11
|
+
|
|
12
|
+
export type PdsFetchHandler = (
|
|
13
|
+
request: WorkersRequest,
|
|
14
|
+
env: Env,
|
|
15
|
+
ctx: ExecutionContext
|
|
16
|
+
) => Promise<WorkersResponse>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Returns the Alteran PDS Worker fetch handler so downstream apps can
|
|
20
|
+
* compose it inside their own Cloudflare Worker entrypoint.
|
|
21
|
+
*/
|
|
22
|
+
export function createPdsFetchHandler(): PdsFetchHandler {
|
|
23
|
+
return async function fetch(request: WorkersRequest, env: Env, ctx: ExecutionContext) {
|
|
24
|
+
try {
|
|
25
|
+
validateConfigOrThrow(env);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return new Response(
|
|
28
|
+
JSON.stringify({
|
|
29
|
+
error: 'ConfigurationError',
|
|
30
|
+
message: error instanceof Error ? error.message : 'Invalid configuration',
|
|
31
|
+
}),
|
|
32
|
+
{
|
|
33
|
+
status: 500,
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
}
|
|
36
|
+
) as unknown as WorkersResponse;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await seed(env.DB, env.PDS_DID ?? 'did:example:single-user');
|
|
40
|
+
|
|
41
|
+
const url = new URL(request.url);
|
|
42
|
+
if (url.pathname === '/xrpc/com.atproto.sync.subscribeRepos') {
|
|
43
|
+
const upgrade = request.headers.get('upgrade');
|
|
44
|
+
if (upgrade !== 'websocket') {
|
|
45
|
+
return new Response('Expected websocket', { status: 426 }) as unknown as WorkersResponse;
|
|
46
|
+
}
|
|
47
|
+
if (!env.SEQUENCER) {
|
|
48
|
+
return new Response('Sequencer not configured', { status: 503 }) as unknown as WorkersResponse;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const id = env.SEQUENCER.idFromName('default');
|
|
52
|
+
const stub = env.SEQUENCER.get(id);
|
|
53
|
+
return (await stub.fetch(request as any)) as unknown as WorkersResponse;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const locals: any = { runtime: { env, ctx, request } };
|
|
57
|
+
return (await onRequest(locals as any, async () => await handle(locals as any))) as unknown as WorkersResponse;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { onRequest };
|
|
62
|
+
export { seed };
|
|
63
|
+
export { validateConfigOrThrow };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createPdsFetchHandler, type PdsFetchHandler } from '../src/worker/runtime';
|
|
2
|
+
export { Sequencer } from '../src/worker/sequencer';
|
|
3
|
+
export { onRequest } from '../src/middleware';
|
|
4
|
+
export { seed } from '../src/db/seed';
|
|
5
|
+
export { validateConfigOrThrow } from '../src/lib/config';
|