@haverstack/server 0.8.0 → 0.9.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/dist/app.d.ts +8 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +70 -0
- package/dist/app.js.map +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +115 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/changeStreams.d.ts +17 -0
- package/dist/lib/changeStreams.d.ts.map +1 -0
- package/{src/lib/changeStreams.ts → dist/lib/changeStreams.js} +12 -12
- package/dist/lib/changeStreams.js.map +1 -0
- package/dist/lib/frameGate.d.ts +26 -0
- package/dist/lib/frameGate.d.ts.map +1 -0
- package/dist/lib/frameGate.js +44 -0
- package/dist/lib/frameGate.js.map +1 -0
- package/dist/lib/json.d.ts +10 -0
- package/dist/lib/json.d.ts.map +1 -0
- package/dist/lib/json.js +27 -0
- package/dist/lib/json.js.map +1 -0
- package/dist/lib/nonceStore.d.ts +50 -0
- package/dist/lib/nonceStore.d.ts.map +1 -0
- package/dist/lib/nonceStore.js +96 -0
- package/dist/lib/nonceStore.js.map +1 -0
- package/dist/lib/queryLimit.d.ts +4 -0
- package/dist/lib/queryLimit.d.ts.map +1 -0
- package/{src/lib/queryLimit.ts → dist/lib/queryLimit.js} +5 -6
- package/dist/lib/queryLimit.js.map +1 -0
- package/dist/lib/queryWorker/pool.d.ts +60 -0
- package/dist/lib/queryWorker/pool.d.ts.map +1 -0
- package/dist/lib/queryWorker/pool.js +278 -0
- package/dist/lib/queryWorker/pool.js.map +1 -0
- package/{src/lib/queryWorker/protocol.ts → dist/lib/queryWorker/protocol.d.ts} +21 -18
- package/dist/lib/queryWorker/protocol.d.ts.map +1 -0
- package/dist/lib/queryWorker/protocol.js +2 -0
- package/dist/lib/queryWorker/protocol.js.map +1 -0
- package/dist/lib/queryWorker/worker.d.ts +2 -0
- package/dist/lib/queryWorker/worker.d.ts.map +1 -0
- package/{src/lib/queryWorker/worker.ts → dist/lib/queryWorker/worker.js} +19 -22
- package/dist/lib/queryWorker/worker.js.map +1 -0
- package/dist/lib/resumeBuffer.d.ts +143 -0
- package/dist/lib/resumeBuffer.d.ts.map +1 -0
- package/dist/lib/resumeBuffer.js +216 -0
- package/dist/lib/resumeBuffer.js.map +1 -0
- package/dist/lib/resumeCursor.d.ts +16 -0
- package/dist/lib/resumeCursor.d.ts.map +1 -0
- package/{src/lib/resumeCursor.ts → dist/lib/resumeCursor.js} +22 -24
- package/dist/lib/resumeCursor.js.map +1 -0
- package/dist/lib/tokenSweep.d.ts +17 -0
- package/dist/lib/tokenSweep.d.ts.map +1 -0
- package/dist/lib/tokenSweep.js +40 -0
- package/dist/lib/tokenSweep.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +46 -0
- package/dist/main.js.map +1 -0
- package/dist/middleware/auth.d.ts +25 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +74 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/errors.d.ts +17 -0
- package/dist/middleware/errors.d.ts.map +1 -0
- package/dist/middleware/errors.js +64 -0
- package/dist/middleware/errors.js.map +1 -0
- package/dist/routes/attachments.d.ts +5 -0
- package/dist/routes/attachments.d.ts.map +1 -0
- package/dist/routes/attachments.js +104 -0
- package/dist/routes/attachments.js.map +1 -0
- package/dist/routes/auth.d.ts +6 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +110 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/changes.d.ts +22 -0
- package/dist/routes/changes.d.ts.map +1 -0
- package/dist/routes/changes.js +233 -0
- package/dist/routes/changes.js.map +1 -0
- package/dist/routes/entity.d.ts +5 -0
- package/dist/routes/entity.d.ts.map +1 -0
- package/dist/routes/entity.js +30 -0
- package/dist/routes/entity.js.map +1 -0
- package/dist/routes/health.d.ts +4 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +7 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/records.d.ts +5 -0
- package/dist/routes/records.d.ts.map +1 -0
- package/dist/routes/records.js +231 -0
- package/dist/routes/records.js.map +1 -0
- package/dist/routes/tokens.d.ts +5 -0
- package/dist/routes/tokens.d.ts.map +1 -0
- package/dist/routes/tokens.js +65 -0
- package/dist/routes/tokens.js.map +1 -0
- package/dist/routes/types.d.ts +5 -0
- package/dist/routes/types.d.ts.map +1 -0
- package/dist/routes/types.js +46 -0
- package/dist/routes/types.js.map +1 -0
- package/dist/routes/wellknown.d.ts +6 -0
- package/dist/routes/wellknown.d.ts.map +1 -0
- package/dist/routes/wellknown.js +45 -0
- package/dist/routes/wellknown.js.map +1 -0
- package/dist/shutdown.d.ts +23 -0
- package/dist/shutdown.d.ts.map +1 -0
- package/dist/shutdown.js +32 -0
- package/dist/shutdown.js.map +1 -0
- package/dist/stack.d.ts +20 -0
- package/dist/stack.d.ts.map +1 -0
- package/dist/stack.js +50 -0
- package/dist/stack.js.map +1 -0
- package/dist/testing.d.ts +49 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +130 -0
- package/dist/testing.js.map +1 -0
- package/{src/types.ts → dist/types.d.ts} +5 -5
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/{src/wireError.ts → dist/wireError.d.ts} +2 -9
- package/dist/wireError.d.ts.map +1 -0
- package/dist/wireError.js +13 -0
- package/dist/wireError.js.map +1 -0
- package/package.json +37 -12
- package/.claude/settings.local.json +0 -22
- package/.dockerignore +0 -10
- package/.env.example +0 -90
- package/.github/pull_request_template.md +0 -23
- package/.github/workflows/ci.yml +0 -55
- package/.github/workflows/release.yml +0 -99
- package/.prettierignore +0 -3
- package/.prettierrc +0 -7
- package/AGENTS.md +0 -91
- package/CONTRIBUTING.md +0 -234
- package/Dockerfile +0 -30
- package/SECURITY.md +0 -54
- package/docs/api.md +0 -277
- package/docs/deployment.md +0 -212
- package/eslint.config.mjs +0 -18
- package/pnpm-workspace.yaml +0 -13
- package/src/app.ts +0 -86
- package/src/config.ts +0 -176
- package/src/index.ts +0 -54
- package/src/lib/frameGate.ts +0 -42
- package/src/lib/json.ts +0 -27
- package/src/lib/nonceStore.ts +0 -103
- package/src/lib/queryWorker/pool.ts +0 -324
- package/src/lib/resumeBuffer.ts +0 -268
- package/src/lib/tokenSweep.ts +0 -43
- package/src/middleware/auth.ts +0 -77
- package/src/middleware/errors.ts +0 -73
- package/src/routes/attachments.ts +0 -126
- package/src/routes/auth.ts +0 -137
- package/src/routes/changes.ts +0 -279
- package/src/routes/entity.ts +0 -33
- package/src/routes/health.ts +0 -8
- package/src/routes/records.ts +0 -259
- package/src/routes/tokens.ts +0 -85
- package/src/routes/types.ts +0 -57
- package/src/routes/wellknown.ts +0 -55
- package/src/shutdown.ts +0 -56
- package/src/stack.ts +0 -96
- package/tests/app.test.ts +0 -86
- package/tests/changeFeedClient.ts +0 -156
- package/tests/config.test.ts +0 -112
- package/tests/conformance.test.ts +0 -2322
- package/tests/fixtures/blockingWorker.ts +0 -27
- package/tests/fixtures/brokenWorker.ts +0 -7
- package/tests/lib/changeFeedClient.test.ts +0 -126
- package/tests/lib/frameGate.test.ts +0 -93
- package/tests/lib/queryLimit.test.ts +0 -33
- package/tests/lib/queryWorker.test.ts +0 -60
- package/tests/lib/queryWorkerPool.test.ts +0 -129
- package/tests/lib/resumeBuffer.test.ts +0 -341
- package/tests/lib/resumeCursor.test.ts +0 -46
- package/tests/middleware/errors.test.ts +0 -176
- package/tests/routes/associations.test.ts +0 -170
- package/tests/routes/attachments.test.ts +0 -621
- package/tests/routes/auth.test.ts +0 -448
- package/tests/routes/changes.test.ts +0 -567
- package/tests/routes/contentQuery.test.ts +0 -68
- package/tests/routes/entity.test.ts +0 -170
- package/tests/routes/groups.test.ts +0 -69
- package/tests/routes/records.test.ts +0 -1965
- package/tests/routes/relatedToQuery.test.ts +0 -182
- package/tests/routes/tokens.test.ts +0 -157
- package/tests/routes/tombstones.test.ts +0 -93
- package/tests/routes/types.test.ts +0 -129
- package/tests/routes/versions.test.ts +0 -177
- package/tests/routes/wellknown.test.ts +0 -71
- package/tests/setup.ts +0 -154
- package/tests/shutdown.test.ts +0 -121
- package/tests/stack.test.ts +0 -198
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -12
package/dist/stack.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { LocalAdapter, NativeTokenStore, defaultTokenStorePath } from '@haverstack/adapter-local';
|
|
2
|
+
import { ARTICLE, BOOKMARK, CONTACT, NOTE, PAGE, PHOTO, PLACE, TASK, defineCommonsTypes, } from '@haverstack/commons';
|
|
3
|
+
import { Stack } from '@haverstack/core';
|
|
4
|
+
import { AuthNonceStore, defaultNonceStorePath } from './lib/nonceStore.js';
|
|
5
|
+
import { QueryWorkerPool } from './lib/queryWorker/pool.js';
|
|
6
|
+
import { ChangeStreamRegistry } from './lib/changeStreams.js';
|
|
7
|
+
export async function initStack(config, logger) {
|
|
8
|
+
// openOrInitialize() decides between open and create without a TOCTOU
|
|
9
|
+
// gap. entityId goes in as a lazy provider so openOrInitialize()'s own
|
|
10
|
+
// owner-mismatch check, which throws, never runs on the open path —
|
|
11
|
+
// ENTITY_ID divergence is a warning below, not a failure — and so a
|
|
12
|
+
// missing ENTITY_ID is an error only when creating a database.
|
|
13
|
+
const adapter = await LocalAdapter.openOrInitialize({
|
|
14
|
+
path: config.dbPath,
|
|
15
|
+
entityId: () => {
|
|
16
|
+
if (!config.entityId) {
|
|
17
|
+
throw new Error('ENTITY_ID is required when initializing a new database (DB_PATH does not exist yet)');
|
|
18
|
+
}
|
|
19
|
+
return config.entityId;
|
|
20
|
+
},
|
|
21
|
+
timezone: config.timezone,
|
|
22
|
+
});
|
|
23
|
+
if (config.entityId && config.entityId !== adapter.ownerEntityId) {
|
|
24
|
+
logger.warn({ configuredEntityId: config.entityId, storedOwnerEntityId: adapter.ownerEntityId }, "ENTITY_ID does not match the stack's stored owner; the configured value is ignored");
|
|
25
|
+
}
|
|
26
|
+
const stack = await Stack.create(adapter, config.ownerName
|
|
27
|
+
? { ownerProfile: { name: config.ownerName, handle: config.ownerHandle ?? undefined } }
|
|
28
|
+
: undefined);
|
|
29
|
+
// Opt-in (SEED_COMMONS_TYPES). defineType() is idempotent — an identical
|
|
30
|
+
// schema is a no-op preserving createdAt — so this is safe every boot.
|
|
31
|
+
if (config.seedCommonsTypes) {
|
|
32
|
+
await defineCommonsTypes(stack, [NOTE, BOOKMARK, TASK, CONTACT, ARTICLE, PLACE, PAGE, PHOTO]);
|
|
33
|
+
}
|
|
34
|
+
// Composed as a separate part rather than sniffed off the adapter: auth
|
|
35
|
+
// material lives in its own file beside stack.db (not inside the
|
|
36
|
+
// portable stack export) per docs/spec/wire-format.md § Authentication.
|
|
37
|
+
const tokens = await NativeTokenStore.open({ path: defaultTokenStorePath(config.dbPath) });
|
|
38
|
+
const nonces = AuthNonceStore.open(defaultNonceStorePath(config.dbPath));
|
|
39
|
+
// Spawned only once the adapter has opened and seeded stack.db: each
|
|
40
|
+
// worker opens its own connection to that same file, and idempotent
|
|
41
|
+
// seeding makes the race survivable rather than worth running.
|
|
42
|
+
const queryWorker = new QueryWorkerPool({
|
|
43
|
+
init: { dbPath: config.dbPath },
|
|
44
|
+
poolSize: config.queryWorkerPoolSize,
|
|
45
|
+
queueLimit: config.queryQueueLimit,
|
|
46
|
+
logger,
|
|
47
|
+
});
|
|
48
|
+
return { adapter, stack, tokens, nonces, queryWorker, changeStreams: new ChangeStreamRegistry() };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,EACL,OAAO,EACP,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,KAAK,EACL,IAAI,EACJ,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAIzC,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAqB9D,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,MAAc;IAC5D,sEAAsE;IACtE,uEAAuE;IACvE,oEAAoE;IACpE,oEAAoE;IACpE,+DAA+D;IAC/D,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,gBAAgB,CAAC;QAClD,IAAI,EAAE,MAAM,CAAC,MAAM;QACnB,QAAQ,EAAE,GAAG,EAAE;YACb,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CACb,qFAAqF,CACtF,CAAC;YACJ,CAAC;YACD,OAAO,MAAM,CAAC,QAAQ,CAAC;QACzB,CAAC;QACD,QAAQ,EAAE,MAAM,CAAC,QAAQ;KAC1B,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC;QACjE,MAAM,CAAC,IAAI,CACT,EAAE,kBAAkB,EAAE,MAAM,CAAC,QAAQ,EAAE,mBAAmB,EAAE,OAAO,CAAC,aAAa,EAAE,EACnF,oFAAoF,CACrF,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,MAAM,CAC9B,OAAO,EACP,MAAM,CAAC,SAAS;QACd,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,IAAI,SAAS,EAAE,EAAE;QACvF,CAAC,CAAC,SAAS,CACd,CAAC;IAEF,yEAAyE;IACzE,uEAAuE;IACvE,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC5B,MAAM,kBAAkB,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAChG,CAAC;IAED,wEAAwE;IACxE,iEAAiE;IACjE,wEAAwE;IACxE,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAEzE,qEAAqE;IACrE,oEAAoE;IACpE,+DAA+D;IAC/D,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC;QACtC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAC/B,QAAQ,EAAE,MAAM,CAAC,mBAAmB;QACpC,UAAU,EAAE,MAAM,CAAC,eAAe;QAClC,MAAM;KACP,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,oBAAoB,EAAE,EAAE,CAAC;AACpG,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-only building blocks for driving a real @haverstack/server. This
|
|
3
|
+
* package's own route tests use `createTestContext`/`testConfig` with
|
|
4
|
+
* `createApp` + Hono's in-process `app.request()` (no socket — see
|
|
5
|
+
* tests/setup.ts's `buildTestApp`/`req`); `startTestServer` goes one step
|
|
6
|
+
* further and actually listens, for a consumer whose client (APIAdapter or
|
|
7
|
+
* otherwise) needs a real URL to fetch against rather than a mocked one.
|
|
8
|
+
* Not for production use: fixed entity/token values, single-worker pool.
|
|
9
|
+
*/
|
|
10
|
+
import type { Logger } from 'pino';
|
|
11
|
+
import type { Config } from './config.js';
|
|
12
|
+
import type { StackContext } from './stack.js';
|
|
13
|
+
export declare const TEST_ENTITY_ID = "did:key:test-entity-id-00000001";
|
|
14
|
+
export declare const TEST_TOKEN = "test-bearer-token";
|
|
15
|
+
export declare const OTHER_ENTITY_ID = "did:key:other-entity-id-00000002";
|
|
16
|
+
export declare const TEST_BASE_URL = "https://stack.example.com";
|
|
17
|
+
export declare const logger: Logger;
|
|
18
|
+
/**
|
|
19
|
+
* Each caller gets its own isolated temp directory so the SQLiteAdapter's
|
|
20
|
+
* sibling `attachments/` folder never collides between parallel test runs.
|
|
21
|
+
*/
|
|
22
|
+
export declare function tempDbPath(): string;
|
|
23
|
+
export type TestContextOpts = {
|
|
24
|
+
/**
|
|
25
|
+
* IANA timezone string, or `undefined` to opt out of one. Note: a plain
|
|
26
|
+
* default parameter can't tell "omitted" from "explicitly undefined"
|
|
27
|
+
* (both trigger the default), so this must be an options bag — pass
|
|
28
|
+
* `{ timezone: undefined }` deliberately, not the bare value.
|
|
29
|
+
*/
|
|
30
|
+
timezone?: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function createTestContext(dbPath: string, opts?: TestContextOpts): Promise<StackContext>;
|
|
33
|
+
export declare function testConfig(dbPath: string, opts?: TestContextOpts): Config;
|
|
34
|
+
export type TestServer = {
|
|
35
|
+
/** The real, reachable base URL — pass straight to APIAdapter.open({ url }). */
|
|
36
|
+
url: string;
|
|
37
|
+
ctx: StackContext;
|
|
38
|
+
config: Config;
|
|
39
|
+
/** Stops accepting connections and releases every resource, including the temp db. */
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Spins up a real, listening @haverstack/server on an ephemeral localhost
|
|
44
|
+
* port, backed by a throwaway LocalAdapter file — for exercising an HTTP
|
|
45
|
+
* client against the actual wire protocol rather than a mocked fetch.
|
|
46
|
+
* Owner auth: `Authorization: Bearer ${TEST_TOKEN}`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function startTestServer(opts?: TestContextOpts): Promise<TestServer>;
|
|
49
|
+
//# sourceMappingURL=testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAMnC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,eAAO,MAAM,cAAc,oCAAoC,CAAC;AAChE,eAAO,MAAM,UAAU,sBAAsB,CAAC;AAC9C,eAAO,MAAM,eAAe,qCAAqC,CAAC;AAMlE,eAAO,MAAM,aAAa,8BAA8B,CAAC;AAEzD,eAAO,MAAM,MAAM,EAAE,MAAkC,CAAC;AAExD;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,eAAqC,GAC1C,OAAO,CAAC,YAAY,CAAC,CAgBvB;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,eAAqC,GAAG,MAAM,CAoB9F;AAkBD,MAAM,MAAM,UAAU,GAAG;IACvB,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,YAAY,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,IAAI,GAAE,eAAqC,GAC1C,OAAO,CAAC,UAAU,CAAC,CA0BrB"}
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-only building blocks for driving a real @haverstack/server. This
|
|
3
|
+
* package's own route tests use `createTestContext`/`testConfig` with
|
|
4
|
+
* `createApp` + Hono's in-process `app.request()` (no socket — see
|
|
5
|
+
* tests/setup.ts's `buildTestApp`/`req`); `startTestServer` goes one step
|
|
6
|
+
* further and actually listens, for a consumer whose client (APIAdapter or
|
|
7
|
+
* otherwise) needs a real URL to fetch against rather than a mocked one.
|
|
8
|
+
* Not for production use: fixed entity/token values, single-worker pool.
|
|
9
|
+
*/
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { dirname, join } from 'node:path';
|
|
12
|
+
import { randomBytes } from 'node:crypto';
|
|
13
|
+
import { rm } from 'node:fs/promises';
|
|
14
|
+
import { mkdirSync } from 'node:fs';
|
|
15
|
+
import { createServer } from 'node:net';
|
|
16
|
+
import { serve } from '@hono/node-server';
|
|
17
|
+
import { LocalAdapter, NativeTokenStore, defaultTokenStorePath } from '@haverstack/adapter-local';
|
|
18
|
+
import { Stack } from '@haverstack/core';
|
|
19
|
+
import { authOriginFromUrl } from '@haverstack/core/wire';
|
|
20
|
+
import pino from 'pino';
|
|
21
|
+
import { createApp } from './app.js';
|
|
22
|
+
import { createShutdownHandler } from './shutdown.js';
|
|
23
|
+
import { AuthNonceStore, defaultNonceStorePath } from './lib/nonceStore.js';
|
|
24
|
+
import { QueryWorkerPool } from './lib/queryWorker/pool.js';
|
|
25
|
+
import { ChangeStreamRegistry } from './lib/changeStreams.js';
|
|
26
|
+
export const TEST_ENTITY_ID = 'did:key:test-entity-id-00000001';
|
|
27
|
+
export const TEST_TOKEN = 'test-bearer-token';
|
|
28
|
+
export const OTHER_ENTITY_ID = 'did:key:other-entity-id-00000002';
|
|
29
|
+
// Matches @haverstack/conformance-fixtures' AUTH_FIXTURE_ORIGIN — the auth
|
|
30
|
+
// handshake fixtures carry real signatures over this exact origin, so
|
|
31
|
+
// anything replaying them (createTestContext/testConfig, in-process
|
|
32
|
+
// app.request()) must present itself as it. startTestServer, which actually
|
|
33
|
+
// listens, uses its own real origin instead — see below.
|
|
34
|
+
export const TEST_BASE_URL = 'https://stack.example.com';
|
|
35
|
+
export const logger = pino({ level: 'silent' });
|
|
36
|
+
/**
|
|
37
|
+
* Each caller gets its own isolated temp directory so the SQLiteAdapter's
|
|
38
|
+
* sibling `attachments/` folder never collides between parallel test runs.
|
|
39
|
+
*/
|
|
40
|
+
export function tempDbPath() {
|
|
41
|
+
const dir = join(tmpdir(), `haverstack-test-${randomBytes(8).toString('hex')}`);
|
|
42
|
+
mkdirSync(dir, { recursive: true });
|
|
43
|
+
return join(dir, 'stack.db');
|
|
44
|
+
}
|
|
45
|
+
export async function createTestContext(dbPath, opts = { timezone: 'UTC' }) {
|
|
46
|
+
const adapter = await LocalAdapter.initialize({
|
|
47
|
+
path: dbPath,
|
|
48
|
+
entityId: TEST_ENTITY_ID,
|
|
49
|
+
...(opts.timezone !== undefined && { timezone: opts.timezone }),
|
|
50
|
+
});
|
|
51
|
+
const stack = await Stack.create(adapter);
|
|
52
|
+
const tokens = await NativeTokenStore.open({ path: defaultTokenStorePath(dbPath) });
|
|
53
|
+
const nonces = AuthNonceStore.open(defaultNonceStorePath(dbPath));
|
|
54
|
+
const queryWorker = new QueryWorkerPool({
|
|
55
|
+
init: { dbPath },
|
|
56
|
+
poolSize: 1,
|
|
57
|
+
queueLimit: 64,
|
|
58
|
+
logger,
|
|
59
|
+
});
|
|
60
|
+
return { adapter, stack, tokens, nonces, queryWorker, changeStreams: new ChangeStreamRegistry() };
|
|
61
|
+
}
|
|
62
|
+
export function testConfig(dbPath, opts = { timezone: 'UTC' }) {
|
|
63
|
+
return {
|
|
64
|
+
port: 3000,
|
|
65
|
+
dbPath,
|
|
66
|
+
entityId: TEST_ENTITY_ID,
|
|
67
|
+
ownerName: null,
|
|
68
|
+
ownerHandle: null,
|
|
69
|
+
timezone: opts.timezone,
|
|
70
|
+
ownerToken: TEST_TOKEN,
|
|
71
|
+
corsOrigins: '*',
|
|
72
|
+
baseUrl: TEST_BASE_URL,
|
|
73
|
+
authOrigin: authOriginFromUrl(TEST_BASE_URL),
|
|
74
|
+
maxAttachmentBytes: 50 * 1024 * 1024,
|
|
75
|
+
maxContentBytes: 1 * 1024 * 1024,
|
|
76
|
+
queryTimeoutMs: 10_000,
|
|
77
|
+
queryWorkerPoolSize: 1,
|
|
78
|
+
queryQueueLimit: 64,
|
|
79
|
+
seedCommonsTypes: false,
|
|
80
|
+
shutdownTimeoutMs: 10_000,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Asks the OS for a free localhost port, then immediately releases it. */
|
|
84
|
+
async function getFreePort() {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
const probe = createServer();
|
|
87
|
+
probe.once('error', reject);
|
|
88
|
+
probe.listen(0, '127.0.0.1', () => {
|
|
89
|
+
const address = probe.address();
|
|
90
|
+
const port = typeof address === 'object' && address ? address.port : undefined;
|
|
91
|
+
probe.close(() => {
|
|
92
|
+
if (port)
|
|
93
|
+
resolve(port);
|
|
94
|
+
else
|
|
95
|
+
reject(new Error('could not allocate a free port'));
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Spins up a real, listening @haverstack/server on an ephemeral localhost
|
|
102
|
+
* port, backed by a throwaway LocalAdapter file — for exercising an HTTP
|
|
103
|
+
* client against the actual wire protocol rather than a mocked fetch.
|
|
104
|
+
* Owner auth: `Authorization: Bearer ${TEST_TOKEN}`.
|
|
105
|
+
*/
|
|
106
|
+
export async function startTestServer(opts = { timezone: 'UTC' }) {
|
|
107
|
+
const dbPath = tempDbPath();
|
|
108
|
+
const ctx = await createTestContext(dbPath, opts);
|
|
109
|
+
const port = await getFreePort();
|
|
110
|
+
const url = `http://127.0.0.1:${port}`;
|
|
111
|
+
const config = {
|
|
112
|
+
...testConfig(dbPath, opts),
|
|
113
|
+
port,
|
|
114
|
+
baseUrl: url,
|
|
115
|
+
authOrigin: authOriginFromUrl(url),
|
|
116
|
+
};
|
|
117
|
+
const app = createApp(ctx, config, logger);
|
|
118
|
+
const server = await new Promise((resolve) => {
|
|
119
|
+
const s = serve({ fetch: app.fetch, port }, () => resolve(s));
|
|
120
|
+
});
|
|
121
|
+
// Reuses the exact production teardown sequence (see src/shutdown.ts) —
|
|
122
|
+
// a short grace period since a stuck test should fail fast, not hang.
|
|
123
|
+
const shutdown = createShutdownHandler(server, ctx, logger, 2_000);
|
|
124
|
+
const close = async () => {
|
|
125
|
+
await shutdown('test-teardown');
|
|
126
|
+
await rm(dirname(dbPath), { recursive: true, force: true }).catch(() => { });
|
|
127
|
+
};
|
|
128
|
+
return { url, ctx, config, close };
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { TokenSession } from '@haverstack/core';
|
|
2
|
-
|
|
3
2
|
/** Hono context variable map shared across all route files. */
|
|
4
3
|
export type AppEnv = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
Variables: {
|
|
5
|
+
auth: TokenSession | null;
|
|
6
|
+
requestId: string;
|
|
7
|
+
};
|
|
9
8
|
};
|
|
9
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,+DAA+D;AAC/D,MAAM,MAAM,MAAM,GAAG;IACnB,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Context } from 'hono';
|
|
2
2
|
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
|
3
3
|
import type { AppEnv } from './types.js';
|
|
4
|
-
|
|
5
4
|
/**
|
|
6
5
|
* `{ error: { code, message } }` responses for failures outside the core
|
|
7
6
|
* `StackError` taxonomy — thrown core errors instead flow through
|
|
@@ -11,11 +10,5 @@ import type { AppEnv } from './types.js';
|
|
|
11
10
|
* docs/spec/wire-format.md § Error responses ("no verified DID behind the
|
|
12
11
|
* request at all" predates any Stack operation).
|
|
13
12
|
*/
|
|
14
|
-
export function wireError(
|
|
15
|
-
|
|
16
|
-
status: ContentfulStatusCode,
|
|
17
|
-
code: string,
|
|
18
|
-
message: string,
|
|
19
|
-
): Response {
|
|
20
|
-
return c.json({ error: { code, message } }, status);
|
|
21
|
-
}
|
|
13
|
+
export declare function wireError(c: Context<AppEnv>, status: ContentfulStatusCode, code: string, message: string): Response;
|
|
14
|
+
//# sourceMappingURL=wireError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wireError.d.ts","sourceRoot":"","sources":["../src/wireError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAClB,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,QAAQ,CAEV"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `{ error: { code, message } }` responses for failures outside the core
|
|
3
|
+
* `StackError` taxonomy — thrown core errors instead flow through
|
|
4
|
+
* `errorMiddleware` via `serializeError()`. `code` is a server-local
|
|
5
|
+
* extension for these (not `WireErrorCode`): `unauthorized` for a missing
|
|
6
|
+
* or invalid bearer token has no core class, per
|
|
7
|
+
* docs/spec/wire-format.md § Error responses ("no verified DID behind the
|
|
8
|
+
* request at all" predates any Stack operation).
|
|
9
|
+
*/
|
|
10
|
+
export function wireError(c, status, code, message) {
|
|
11
|
+
return c.json({ error: { code, message } }, status);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=wireError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wireError.js","sourceRoot":"","sources":["../src/wireError.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CACvB,CAAkB,EAClB,MAA4B,EAC5B,IAAY,EACZ,OAAe;IAEf,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haverstack/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Reference server implementation for Haverstack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=22.9"
|
|
8
8
|
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/haverstack/server.git"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts"
|
|
19
|
+
},
|
|
20
|
+
"./testing": {
|
|
21
|
+
"import": "./dist/testing.js",
|
|
22
|
+
"types": "./dist/testing.d.ts"
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"dev": "tsx watch --env-file-if-exists=.env src/main.ts",
|
|
34
|
+
"build": "tsc -p tsconfig.build.json",
|
|
35
|
+
"start": "node --env-file-if-exists=.env dist/main.js",
|
|
36
|
+
"prepublishOnly": "pnpm run build",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"lint": "eslint src tests",
|
|
40
|
+
"format": "prettier --write .",
|
|
41
|
+
"format:check": "prettier --check ."
|
|
42
|
+
},
|
|
9
43
|
"dependencies": {
|
|
10
44
|
"@haverstack/adapter-local": "^0.30.0",
|
|
11
45
|
"@haverstack/commons": "^0.25.0",
|
|
@@ -29,14 +63,5 @@
|
|
|
29
63
|
"vite": "^8.2.1",
|
|
30
64
|
"vitest": "^4.1.11"
|
|
31
65
|
},
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
"build": "tsc -p tsconfig.build.json",
|
|
35
|
-
"start": "node --env-file-if-exists=.env dist/index.js",
|
|
36
|
-
"test": "vitest run",
|
|
37
|
-
"typecheck": "tsc --noEmit",
|
|
38
|
-
"lint": "eslint src tests",
|
|
39
|
-
"format": "prettier --write .",
|
|
40
|
-
"format:check": "prettier --check ."
|
|
41
|
-
}
|
|
42
|
-
}
|
|
66
|
+
"packageManager": "pnpm@11.24.0+sha512.bd27e345e976dcb0be0b7a1228217b049a817e21b1f355c90dbe7dc46671895a8bc1e6d06c24554505ea93ea0b45f489a27ec1bfbc8de6a9659fca0f16fa0000"
|
|
67
|
+
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(openssl rand *)",
|
|
5
|
-
"Bash(pnpm ls *)",
|
|
6
|
-
"Bash(find / -path \"*haverstack/core*\" -name \"*.js\")",
|
|
7
|
-
"Read(//var/home/jen/Dev/haverstack/core/packages/core/**)",
|
|
8
|
-
"Bash(node -e \"const {generateId} = require\\('./dist/id.js'\\); console.log\\(generateId\\(\\)\\)\")",
|
|
9
|
-
"Bash(node --input-type=module -e \"import {generateId} from '/var/home/jen/Dev/haverstack/core/packages/core/dist/id.js'; console.log\\(generateId\\(\\)\\)\")",
|
|
10
|
-
"Bash(node *)",
|
|
11
|
-
"Bash(echo \"exit: $?\")",
|
|
12
|
-
"Bash(pnpm -v)",
|
|
13
|
-
"Bash(pnpm help *)",
|
|
14
|
-
"Bash(python3 -c ' *)",
|
|
15
|
-
"Bash(gh issue *)",
|
|
16
|
-
"Bash(pnpm typecheck *)",
|
|
17
|
-
"Bash(pnpm test *)",
|
|
18
|
-
"Bash(pnpm lint *)",
|
|
19
|
-
"Bash(pnpm format *)"
|
|
20
|
-
]
|
|
21
|
-
}
|
|
22
|
-
}
|
package/.dockerignore
DELETED
package/.env.example
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# Port to listen on (default: 3000)
|
|
2
|
-
PORT=3000
|
|
3
|
-
|
|
4
|
-
# Path to the SQLite database file.
|
|
5
|
-
# On first run (file does not exist), the database is initialized.
|
|
6
|
-
# On subsequent runs, the existing database is opened.
|
|
7
|
-
DB_PATH=./stack.db
|
|
8
|
-
|
|
9
|
-
# Owner entity ID. Required only on first run when the DB is being initialized.
|
|
10
|
-
# Must be a DID (e.g. "did:key:z6Mk..."). Generate one with:
|
|
11
|
-
# node -e "require('@haverstack/core/did').generateDidKeypair().then(k => console.log(k.did))"
|
|
12
|
-
# Keep the corresponding private key — this only prints the public DID.
|
|
13
|
-
ENTITY_ID=
|
|
14
|
-
|
|
15
|
-
# Owner's display name (optional). When set, the server ensures the owner's
|
|
16
|
-
# own _entity profile record exists on every open — safe to leave set
|
|
17
|
-
# permanently. No profile record is created if omitted.
|
|
18
|
-
OWNER_NAME=
|
|
19
|
-
|
|
20
|
-
# Owner's handle (optional). Only used alongside OWNER_NAME.
|
|
21
|
-
OWNER_HANDLE=
|
|
22
|
-
|
|
23
|
-
# IANA timezone string (optional). Used only on first run. No default — an
|
|
24
|
-
# absent timezone stays absent (omitted from discovery), rather than
|
|
25
|
-
# asserting a timezone the stack was never actually given.
|
|
26
|
-
TIMEZONE=
|
|
27
|
-
|
|
28
|
-
# Master bearer token for the stack owner. This is a break-glass credential:
|
|
29
|
-
# the DID challenge-response handshake lets a client holding the owner's key
|
|
30
|
-
# authenticate without it, but OWNER_TOKEN remains required to bootstrap the
|
|
31
|
-
# server and manage other tokens via POST /tokens. Keep this secret and
|
|
32
|
-
# treat it like a root password. All other tokens are issued via the API
|
|
33
|
-
# and stored in the DB.
|
|
34
|
-
# Generate with `openssl rand -hex 32`
|
|
35
|
-
OWNER_TOKEN=
|
|
36
|
-
|
|
37
|
-
# Allowed CORS origins. Default: empty (no cross-origin access).
|
|
38
|
-
# Set to * to allow all origins, or a comma-separated list:
|
|
39
|
-
# CORS_ORIGINS=https://app.example.com,https://admin.example.com
|
|
40
|
-
# See docs/deployment.md for guidance.
|
|
41
|
-
CORS_ORIGINS=
|
|
42
|
-
|
|
43
|
-
# Canonical public base URL of this server. Required: it's the origin the
|
|
44
|
-
# DID challenge-response handshake (POST /auth/challenge, POST /auth/token)
|
|
45
|
-
# signs and verifies against, and MUST be this server's own public origin —
|
|
46
|
-
# never auto-detected from a request header, which a client controls.
|
|
47
|
-
# Example: https://stack.example.com
|
|
48
|
-
BASE_URL=
|
|
49
|
-
|
|
50
|
-
# Maximum upload size for attachments in bytes (default: 52428800 = 50 MB).
|
|
51
|
-
MAX_ATTACHMENT_BYTES=52428800
|
|
52
|
-
|
|
53
|
-
# Maximum size of a JSON request body in bytes — record bodies, PATCH merge
|
|
54
|
-
# patches, and every other JSON-accepting route (default: 1048576 = 1 MB).
|
|
55
|
-
# Applies globally, except POST /attachments, which is bounded by
|
|
56
|
-
# MAX_ATTACHMENT_BYTES instead. Declared to clients via maxContentBytes in
|
|
57
|
-
# GET /.well-known/stack.
|
|
58
|
-
MAX_CONTENT_BYTES=1048576
|
|
59
|
-
|
|
60
|
-
# Deadline in milliseconds for a GET /records or POST /records/query search
|
|
61
|
-
# (default: 10000 = 10s). Bounds execution only — the clock starts when the
|
|
62
|
-
# search reaches a worker, so waiting for a free worker makes a request slow,
|
|
63
|
-
# never failed. A query still executing past it is abandoned with 503 (code
|
|
64
|
-
# "timeout"). See docs/deployment.md#bounding-query-cost.
|
|
65
|
-
QUERY_TIMEOUT_MS=10000
|
|
66
|
-
|
|
67
|
-
# Number of worker threads a slow search can occupy without blocking other
|
|
68
|
-
# requests (default: 2, max 32). See docs/deployment.md#bounding-query-cost.
|
|
69
|
-
QUERY_WORKER_POOL_SIZE=2
|
|
70
|
-
|
|
71
|
-
# How many searches may queue for a free worker before the server sheds load
|
|
72
|
-
# with 503 (code "timeout") rather than queueing further (default: 64). This
|
|
73
|
-
# is what bounds the waiting that QUERY_TIMEOUT_MS deliberately does not.
|
|
74
|
-
# See docs/deployment.md#bounding-query-cost.
|
|
75
|
-
QUERY_QUEUE_LIMIT=64
|
|
76
|
-
|
|
77
|
-
# Register the Schema Commons types (org.haverstack/note, bookmark, task,
|
|
78
|
-
# contact, article, place, page, photo) from @haverstack/commons on startup
|
|
79
|
-
# (default: false). Off by default because the package is Draft status and
|
|
80
|
-
# registering types is not free — they show up in GET /types and in every
|
|
81
|
-
# app's type cache. See docs/deployment.md#schema-commons-seeding.
|
|
82
|
-
SEED_COMMONS_TYPES=false
|
|
83
|
-
|
|
84
|
-
# How long shutdown waits (in milliseconds) for open/keep-alive connections
|
|
85
|
-
# to drain on server.close() before forcing them closed and finishing the
|
|
86
|
-
# flush/close sequence anyway (default: 10000 = 10s).
|
|
87
|
-
SHUTDOWN_TIMEOUT_MS=10000
|
|
88
|
-
|
|
89
|
-
# See docs/deployment.md for production deployment guidance (TLS, CORS,
|
|
90
|
-
# rate limiting, and public endpoint exposure).
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
## Summary
|
|
2
|
-
|
|
3
|
-
<!-- What changed, and why. Link any issue this closes. -->
|
|
4
|
-
|
|
5
|
-
## Docs
|
|
6
|
-
|
|
7
|
-
<!-- Does this change observable behavior — a route, status code, error code,
|
|
8
|
-
auth rule, or environment variable? If so, link the docs/ sections updated
|
|
9
|
-
in this PR (docs/api.md, docs/deployment.md, README's config table,
|
|
10
|
-
.env.example). If not, say "No behavior change."
|
|
11
|
-
|
|
12
|
-
If the change is to the wire contract itself rather than to this server's
|
|
13
|
-
implementation of it, the spec and conformance fixtures live in
|
|
14
|
-
haverstack/core — link that PR here. -->
|
|
15
|
-
|
|
16
|
-
## Verification
|
|
17
|
-
|
|
18
|
-
<!-- What you ran, and anything you checked by hand.
|
|
19
|
-
pnpm run format:check && pnpm run lint && pnpm typecheck && pnpm test -->
|
|
20
|
-
|
|
21
|
-
## Notes for reviewers
|
|
22
|
-
|
|
23
|
-
<!-- Optional: trade-offs, alternatives considered, follow-up work worth filing. -->
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
format:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v4
|
|
13
|
-
- uses: pnpm/action-setup@v4
|
|
14
|
-
- uses: actions/setup-node@v4
|
|
15
|
-
with:
|
|
16
|
-
node-version: 22
|
|
17
|
-
cache: pnpm
|
|
18
|
-
- run: pnpm install --frozen-lockfile
|
|
19
|
-
- run: pnpm run format:check
|
|
20
|
-
|
|
21
|
-
lint:
|
|
22
|
-
runs-on: ubuntu-latest
|
|
23
|
-
steps:
|
|
24
|
-
- uses: actions/checkout@v4
|
|
25
|
-
- uses: pnpm/action-setup@v4
|
|
26
|
-
- uses: actions/setup-node@v4
|
|
27
|
-
with:
|
|
28
|
-
node-version: 22
|
|
29
|
-
cache: pnpm
|
|
30
|
-
- run: pnpm install --frozen-lockfile
|
|
31
|
-
- run: pnpm run lint
|
|
32
|
-
|
|
33
|
-
typecheck:
|
|
34
|
-
runs-on: ubuntu-latest
|
|
35
|
-
steps:
|
|
36
|
-
- uses: actions/checkout@v4
|
|
37
|
-
- uses: pnpm/action-setup@v4
|
|
38
|
-
- uses: actions/setup-node@v4
|
|
39
|
-
with:
|
|
40
|
-
node-version: 22
|
|
41
|
-
cache: pnpm
|
|
42
|
-
- run: pnpm install --frozen-lockfile
|
|
43
|
-
- run: pnpm run typecheck
|
|
44
|
-
|
|
45
|
-
test:
|
|
46
|
-
runs-on: ubuntu-latest
|
|
47
|
-
steps:
|
|
48
|
-
- uses: actions/checkout@v4
|
|
49
|
-
- uses: pnpm/action-setup@v4
|
|
50
|
-
- uses: actions/setup-node@v4
|
|
51
|
-
with:
|
|
52
|
-
node-version: 22
|
|
53
|
-
cache: pnpm
|
|
54
|
-
- run: pnpm install --frozen-lockfile
|
|
55
|
-
- run: pnpm test
|