@haverstack/server 0.8.0 → 0.9.1

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 +108 -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 +42 -17
  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
@@ -1,170 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { buildTestApp, req, TEST_TOKEN, type TestApp } from '../setup.js';
3
-
4
- const TYPE_ID = 'com.example.test/post@1';
5
-
6
- describe('Associations', () => {
7
- let t: TestApp;
8
- beforeEach(async () => {
9
- t = await buildTestApp();
10
- await t.ctx.stack.defineType(TYPE_ID, 'Post', {
11
- text: { kind: 'text' as const, required: true as const },
12
- });
13
- });
14
- afterEach(async () => {
15
- await t.cleanup();
16
- });
17
-
18
- async function seedRecord() {
19
- return t.ctx.stack.create(TYPE_ID, { text: 'Hello' });
20
- }
21
-
22
- it('POST adds a tag association and answers with the updated record', async () => {
23
- const record = await seedRecord();
24
- const { status, data } = await req(t.app, 'POST', `/records/${record.id}/associations`, {
25
- token: TEST_TOKEN,
26
- body: { kind: 'tag', label: 'starred' },
27
- });
28
- expect(status).toBe(200);
29
- expect((data as Record<string, unknown>).associations).toEqual([
30
- { kind: 'tag', label: 'starred' },
31
- ]);
32
- });
33
-
34
- it('POST adds a relationship association with a record-scope target and answers with the updated record', async () => {
35
- const record = await seedRecord();
36
- const other = await seedRecord();
37
- const target = { scope: 'record' as const, recordId: other.id };
38
- const { status, data } = await req(t.app, 'POST', `/records/${record.id}/associations`, {
39
- token: TEST_TOKEN,
40
- body: { kind: 'relationship', label: 'reply-to', target },
41
- });
42
- expect(status).toBe(200);
43
- expect((data as Record<string, unknown>).associations).toEqual([
44
- { kind: 'relationship', label: 'reply-to', target },
45
- ]);
46
- });
47
-
48
- it.each([
49
- ['entity', { scope: 'entity', entityId: 'did:key:z6MkAlice' }],
50
- ['external', { scope: 'external', ns: 'atproto', id: 'at://did:plc:abc/app.bsky.feed.post/1' }],
51
- ] as const)(
52
- 'POST adds a relationship association with a %s-scope target and answers with the updated record',
53
- async (_scope, target) => {
54
- const record = await seedRecord();
55
- const { status, data } = await req(t.app, 'POST', `/records/${record.id}/associations`, {
56
- token: TEST_TOKEN,
57
- body: { kind: 'relationship', label: 'reply-to', target },
58
- });
59
- expect(status).toBe(200);
60
- expect((data as Record<string, unknown>).associations).toEqual([
61
- { kind: 'relationship', label: 'reply-to', target },
62
- ]);
63
- },
64
- );
65
-
66
- it('GET returns all associations', async () => {
67
- const record = await seedRecord();
68
- await t.ctx.adapter.associate(record.id, { kind: 'tag', label: 'starred' });
69
- await t.ctx.adapter.associate(record.id, { kind: 'tag', label: 'archived' });
70
- const { status, data } = await req(t.app, 'GET', `/records/${record.id}/associations`, {
71
- token: TEST_TOKEN,
72
- });
73
- expect(status).toBe(200);
74
- expect((data as { associations: unknown[] }).associations).toHaveLength(2);
75
- });
76
-
77
- it('GET ?kind=tag filters by kind', async () => {
78
- const record = await seedRecord();
79
- await t.ctx.adapter.associate(record.id, { kind: 'tag', label: 'starred' });
80
- const { status, data } = await req(
81
- t.app,
82
- 'GET',
83
- `/records/${record.id}/associations?kind=tag`,
84
- { token: TEST_TOKEN },
85
- );
86
- expect(status).toBe(200);
87
- const assocs = (data as { associations: Array<{ kind: string }> }).associations;
88
- expect(assocs.every((a) => a.kind === 'tag')).toBe(true);
89
- });
90
-
91
- it('POST .../associations/delete removes an association and answers with the updated record', async () => {
92
- const record = await seedRecord();
93
- await t.ctx.adapter.associate(record.id, { kind: 'tag', label: 'starred' });
94
- const { status, data } = await req(t.app, 'POST', `/records/${record.id}/associations/delete`, {
95
- token: TEST_TOKEN,
96
- body: { kind: 'tag', label: 'starred' },
97
- });
98
- expect(status).toBe(200);
99
- expect((data as Record<string, unknown>).associations).toBeUndefined();
100
- const after = await t.ctx.adapter.getRecord(record.id);
101
- expect(after?.associations?.some((a) => a.label === 'starred')).toBeFalsy();
102
- });
103
-
104
- it('POST .../associations/delete removes a relationship association regardless of target scope', async () => {
105
- const record = await seedRecord();
106
- const target = { scope: 'entity' as const, entityId: 'did:key:z6MkAlice' };
107
- await t.ctx.adapter.associate(record.id, { kind: 'relationship', label: 'author', target });
108
- const { status, data } = await req(t.app, 'POST', `/records/${record.id}/associations/delete`, {
109
- token: TEST_TOKEN,
110
- body: { kind: 'relationship', label: 'author', target },
111
- });
112
- expect(status).toBe(200);
113
- expect((data as Record<string, unknown>).associations).toBeUndefined();
114
- const after = await t.ctx.adapter.getRecord(record.id);
115
- expect(after?.associations?.some((a) => a.label === 'author')).toBeFalsy();
116
- });
117
-
118
- describe('If-Match / optimistic concurrency', () => {
119
- it('POST /associations succeeds when If-Match names the current version', async () => {
120
- const record = await seedRecord();
121
- const { status } = await req(t.app, 'POST', `/records/${record.id}/associations`, {
122
- token: TEST_TOKEN,
123
- body: { kind: 'tag', label: 'starred' },
124
- headers: { 'If-Match': `"${record.version}"` },
125
- });
126
- expect(status).toBe(200);
127
- });
128
-
129
- it('POST /associations returns 412 version_conflict on an If-Match mismatch', async () => {
130
- const record = await seedRecord();
131
- const { status, data } = await req(t.app, 'POST', `/records/${record.id}/associations`, {
132
- token: TEST_TOKEN,
133
- body: { kind: 'tag', label: 'starred' },
134
- headers: { 'If-Match': `"${record.version + 1}"` },
135
- });
136
- expect(status).toBe(412);
137
- expect((data as { error: { code: string } }).error.code).toBe('version_conflict');
138
- });
139
-
140
- it('POST /associations/delete succeeds when If-Match names the current version', async () => {
141
- const record = await seedRecord();
142
- await t.ctx.adapter.associate(record.id, { kind: 'tag', label: 'starred' });
143
- const current = await t.ctx.adapter.getRecord(record.id);
144
- const { status } = await req(t.app, 'POST', `/records/${record.id}/associations/delete`, {
145
- token: TEST_TOKEN,
146
- body: { kind: 'tag', label: 'starred' },
147
- headers: { 'If-Match': `"${current!.version}"` },
148
- });
149
- expect(status).toBe(200);
150
- });
151
-
152
- it('POST /associations/delete returns 412 version_conflict on an If-Match mismatch', async () => {
153
- const record = await seedRecord();
154
- await t.ctx.adapter.associate(record.id, { kind: 'tag', label: 'starred' });
155
- const current = await t.ctx.adapter.getRecord(record.id);
156
- const { status, data } = await req(
157
- t.app,
158
- 'POST',
159
- `/records/${record.id}/associations/delete`,
160
- {
161
- token: TEST_TOKEN,
162
- body: { kind: 'tag', label: 'starred' },
163
- headers: { 'If-Match': `"${current!.version + 1}"` },
164
- },
165
- );
166
- expect(status).toBe(412);
167
- expect((data as { error: { code: string } }).error.code).toBe('version_conflict');
168
- });
169
- });
170
- });