@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.
Files changed (197) hide show
  1. package/dist/app.d.ts +8 -0
  2. package/dist/app.d.ts.map +1 -0
  3. package/dist/app.js +70 -0
  4. package/dist/app.js.map +1 -0
  5. package/dist/config.d.ts +21 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/config.js +115 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/index.d.ts +16 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +12 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/lib/changeStreams.d.ts +17 -0
  14. package/dist/lib/changeStreams.d.ts.map +1 -0
  15. package/{src/lib/changeStreams.ts → dist/lib/changeStreams.js} +12 -12
  16. package/dist/lib/changeStreams.js.map +1 -0
  17. package/dist/lib/frameGate.d.ts +26 -0
  18. package/dist/lib/frameGate.d.ts.map +1 -0
  19. package/dist/lib/frameGate.js +44 -0
  20. package/dist/lib/frameGate.js.map +1 -0
  21. package/dist/lib/json.d.ts +10 -0
  22. package/dist/lib/json.d.ts.map +1 -0
  23. package/dist/lib/json.js +27 -0
  24. package/dist/lib/json.js.map +1 -0
  25. package/dist/lib/nonceStore.d.ts +50 -0
  26. package/dist/lib/nonceStore.d.ts.map +1 -0
  27. package/dist/lib/nonceStore.js +96 -0
  28. package/dist/lib/nonceStore.js.map +1 -0
  29. package/dist/lib/queryLimit.d.ts +4 -0
  30. package/dist/lib/queryLimit.d.ts.map +1 -0
  31. package/{src/lib/queryLimit.ts → dist/lib/queryLimit.js} +5 -6
  32. package/dist/lib/queryLimit.js.map +1 -0
  33. package/dist/lib/queryWorker/pool.d.ts +60 -0
  34. package/dist/lib/queryWorker/pool.d.ts.map +1 -0
  35. package/dist/lib/queryWorker/pool.js +278 -0
  36. package/dist/lib/queryWorker/pool.js.map +1 -0
  37. package/{src/lib/queryWorker/protocol.ts → dist/lib/queryWorker/protocol.d.ts} +21 -18
  38. package/dist/lib/queryWorker/protocol.d.ts.map +1 -0
  39. package/dist/lib/queryWorker/protocol.js +2 -0
  40. package/dist/lib/queryWorker/protocol.js.map +1 -0
  41. package/dist/lib/queryWorker/worker.d.ts +2 -0
  42. package/dist/lib/queryWorker/worker.d.ts.map +1 -0
  43. package/{src/lib/queryWorker/worker.ts → dist/lib/queryWorker/worker.js} +19 -22
  44. package/dist/lib/queryWorker/worker.js.map +1 -0
  45. package/dist/lib/resumeBuffer.d.ts +143 -0
  46. package/dist/lib/resumeBuffer.d.ts.map +1 -0
  47. package/dist/lib/resumeBuffer.js +216 -0
  48. package/dist/lib/resumeBuffer.js.map +1 -0
  49. package/dist/lib/resumeCursor.d.ts +16 -0
  50. package/dist/lib/resumeCursor.d.ts.map +1 -0
  51. package/{src/lib/resumeCursor.ts → dist/lib/resumeCursor.js} +22 -24
  52. package/dist/lib/resumeCursor.js.map +1 -0
  53. package/dist/lib/tokenSweep.d.ts +17 -0
  54. package/dist/lib/tokenSweep.d.ts.map +1 -0
  55. package/dist/lib/tokenSweep.js +40 -0
  56. package/dist/lib/tokenSweep.js.map +1 -0
  57. package/dist/main.d.ts +2 -0
  58. package/dist/main.d.ts.map +1 -0
  59. package/dist/main.js +46 -0
  60. package/dist/main.js.map +1 -0
  61. package/dist/middleware/auth.d.ts +25 -0
  62. package/dist/middleware/auth.d.ts.map +1 -0
  63. package/dist/middleware/auth.js +74 -0
  64. package/dist/middleware/auth.js.map +1 -0
  65. package/dist/middleware/errors.d.ts +17 -0
  66. package/dist/middleware/errors.d.ts.map +1 -0
  67. package/dist/middleware/errors.js +64 -0
  68. package/dist/middleware/errors.js.map +1 -0
  69. package/dist/routes/attachments.d.ts +5 -0
  70. package/dist/routes/attachments.d.ts.map +1 -0
  71. package/dist/routes/attachments.js +104 -0
  72. package/dist/routes/attachments.js.map +1 -0
  73. package/dist/routes/auth.d.ts +6 -0
  74. package/dist/routes/auth.d.ts.map +1 -0
  75. package/dist/routes/auth.js +110 -0
  76. package/dist/routes/auth.js.map +1 -0
  77. package/dist/routes/changes.d.ts +22 -0
  78. package/dist/routes/changes.d.ts.map +1 -0
  79. package/dist/routes/changes.js +233 -0
  80. package/dist/routes/changes.js.map +1 -0
  81. package/dist/routes/entity.d.ts +5 -0
  82. package/dist/routes/entity.d.ts.map +1 -0
  83. package/dist/routes/entity.js +30 -0
  84. package/dist/routes/entity.js.map +1 -0
  85. package/dist/routes/health.d.ts +4 -0
  86. package/dist/routes/health.d.ts.map +1 -0
  87. package/dist/routes/health.js +7 -0
  88. package/dist/routes/health.js.map +1 -0
  89. package/dist/routes/records.d.ts +5 -0
  90. package/dist/routes/records.d.ts.map +1 -0
  91. package/dist/routes/records.js +231 -0
  92. package/dist/routes/records.js.map +1 -0
  93. package/dist/routes/tokens.d.ts +5 -0
  94. package/dist/routes/tokens.d.ts.map +1 -0
  95. package/dist/routes/tokens.js +65 -0
  96. package/dist/routes/tokens.js.map +1 -0
  97. package/dist/routes/types.d.ts +5 -0
  98. package/dist/routes/types.d.ts.map +1 -0
  99. package/dist/routes/types.js +46 -0
  100. package/dist/routes/types.js.map +1 -0
  101. package/dist/routes/wellknown.d.ts +6 -0
  102. package/dist/routes/wellknown.d.ts.map +1 -0
  103. package/dist/routes/wellknown.js +45 -0
  104. package/dist/routes/wellknown.js.map +1 -0
  105. package/dist/shutdown.d.ts +23 -0
  106. package/dist/shutdown.d.ts.map +1 -0
  107. package/dist/shutdown.js +32 -0
  108. package/dist/shutdown.js.map +1 -0
  109. package/dist/stack.d.ts +20 -0
  110. package/dist/stack.d.ts.map +1 -0
  111. package/dist/stack.js +50 -0
  112. package/dist/stack.js.map +1 -0
  113. package/dist/testing.d.ts +49 -0
  114. package/dist/testing.d.ts.map +1 -0
  115. package/dist/testing.js +130 -0
  116. package/dist/testing.js.map +1 -0
  117. package/{src/types.ts → dist/types.d.ts} +5 -5
  118. package/dist/types.d.ts.map +1 -0
  119. package/dist/types.js +2 -0
  120. package/dist/types.js.map +1 -0
  121. package/{src/wireError.ts → dist/wireError.d.ts} +2 -9
  122. package/dist/wireError.d.ts.map +1 -0
  123. package/dist/wireError.js +13 -0
  124. package/dist/wireError.js.map +1 -0
  125. package/package.json +37 -12
  126. package/.claude/settings.local.json +0 -22
  127. package/.dockerignore +0 -10
  128. package/.env.example +0 -90
  129. package/.github/pull_request_template.md +0 -23
  130. package/.github/workflows/ci.yml +0 -55
  131. package/.github/workflows/release.yml +0 -99
  132. package/.prettierignore +0 -3
  133. package/.prettierrc +0 -7
  134. package/AGENTS.md +0 -91
  135. package/CONTRIBUTING.md +0 -234
  136. package/Dockerfile +0 -30
  137. package/SECURITY.md +0 -54
  138. package/docs/api.md +0 -277
  139. package/docs/deployment.md +0 -212
  140. package/eslint.config.mjs +0 -18
  141. package/pnpm-workspace.yaml +0 -13
  142. package/src/app.ts +0 -86
  143. package/src/config.ts +0 -176
  144. package/src/index.ts +0 -54
  145. package/src/lib/frameGate.ts +0 -42
  146. package/src/lib/json.ts +0 -27
  147. package/src/lib/nonceStore.ts +0 -103
  148. package/src/lib/queryWorker/pool.ts +0 -324
  149. package/src/lib/resumeBuffer.ts +0 -268
  150. package/src/lib/tokenSweep.ts +0 -43
  151. package/src/middleware/auth.ts +0 -77
  152. package/src/middleware/errors.ts +0 -73
  153. package/src/routes/attachments.ts +0 -126
  154. package/src/routes/auth.ts +0 -137
  155. package/src/routes/changes.ts +0 -279
  156. package/src/routes/entity.ts +0 -33
  157. package/src/routes/health.ts +0 -8
  158. package/src/routes/records.ts +0 -259
  159. package/src/routes/tokens.ts +0 -85
  160. package/src/routes/types.ts +0 -57
  161. package/src/routes/wellknown.ts +0 -55
  162. package/src/shutdown.ts +0 -56
  163. package/src/stack.ts +0 -96
  164. package/tests/app.test.ts +0 -86
  165. package/tests/changeFeedClient.ts +0 -156
  166. package/tests/config.test.ts +0 -112
  167. package/tests/conformance.test.ts +0 -2322
  168. package/tests/fixtures/blockingWorker.ts +0 -27
  169. package/tests/fixtures/brokenWorker.ts +0 -7
  170. package/tests/lib/changeFeedClient.test.ts +0 -126
  171. package/tests/lib/frameGate.test.ts +0 -93
  172. package/tests/lib/queryLimit.test.ts +0 -33
  173. package/tests/lib/queryWorker.test.ts +0 -60
  174. package/tests/lib/queryWorkerPool.test.ts +0 -129
  175. package/tests/lib/resumeBuffer.test.ts +0 -341
  176. package/tests/lib/resumeCursor.test.ts +0 -46
  177. package/tests/middleware/errors.test.ts +0 -176
  178. package/tests/routes/associations.test.ts +0 -170
  179. package/tests/routes/attachments.test.ts +0 -621
  180. package/tests/routes/auth.test.ts +0 -448
  181. package/tests/routes/changes.test.ts +0 -567
  182. package/tests/routes/contentQuery.test.ts +0 -68
  183. package/tests/routes/entity.test.ts +0 -170
  184. package/tests/routes/groups.test.ts +0 -69
  185. package/tests/routes/records.test.ts +0 -1965
  186. package/tests/routes/relatedToQuery.test.ts +0 -182
  187. package/tests/routes/tokens.test.ts +0 -157
  188. package/tests/routes/tombstones.test.ts +0 -93
  189. package/tests/routes/types.test.ts +0 -129
  190. package/tests/routes/versions.test.ts +0 -177
  191. package/tests/routes/wellknown.test.ts +0 -71
  192. package/tests/setup.ts +0 -154
  193. package/tests/shutdown.test.ts +0 -121
  194. package/tests/stack.test.ts +0 -198
  195. package/tsconfig.build.json +0 -9
  196. package/tsconfig.json +0 -19
  197. 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"}
@@ -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
- Variables: {
6
- auth: TokenSession | null;
7
- requestId: string;
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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
- c: Context<AppEnv>,
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.8.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
- "scripts": {
33
- "dev": "tsx watch --env-file-if-exists=.env src/index.ts",
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
@@ -1,10 +0,0 @@
1
- node_modules
2
- dist
3
- .git
4
- .github
5
- tests
6
- *.db
7
- attachments/
8
- .env
9
- .env.local
10
- coverage/
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. -->
@@ -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