@takosjp/yurucommu-core 3.3.0 → 3.4.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 (52) hide show
  1. package/migrations/0022_inbound_dispatch_claims.sql +17 -0
  2. package/package.json +3 -2
  3. package/packages/api/package.json +1 -1
  4. package/packages/api/src/index.ts +1 -0
  5. package/packages/api/src/lib/api/notifications.ts +1 -0
  6. package/packages/api/src/lib/api/posts.ts +1 -0
  7. package/packages/api/src/lib/rtc-client.ts +1 -3
  8. package/packages/api/src/types/call.ts +2 -10
  9. package/packages/api/src/types/index.ts +3 -0
  10. package/packages/api/src/types/realtime.ts +139 -0
  11. package/src/backend/index.ts +54 -12
  12. package/src/backend/lib/delivery/queue-batching.ts +53 -36
  13. package/src/backend/lib/delivery/queue-delivery.ts +3 -3
  14. package/src/backend/lib/delivery/queue.ts +17 -9
  15. package/src/backend/lib/delivery/types.ts +12 -0
  16. package/src/backend/lib/notification-push.ts +2 -2
  17. package/src/backend/lib/oauth-providers.ts +9 -0
  18. package/src/backend/lib/strip-image-metadata.ts +50 -30
  19. package/src/backend/lib/unread-counts.ts +63 -2
  20. package/src/backend/middleware/bearer-auth.ts +24 -9
  21. package/src/backend/public.ts +25 -1
  22. package/src/backend/routes/activitypub/handlers/actor-inbox-handlers.ts +4 -3
  23. package/src/backend/routes/activitypub/handlers/inbox-content-handlers.ts +204 -5
  24. package/src/backend/routes/activitypub/handlers/inbox-follow-handlers.ts +78 -53
  25. package/src/backend/routes/activitypub/handlers/inbox-interaction-handlers.ts +66 -2
  26. package/src/backend/routes/activitypub/handlers/inbox-shared-helpers.ts +35 -12
  27. package/src/backend/routes/activitypub/inbox-addressing.ts +236 -0
  28. package/src/backend/routes/activitypub/inbox-types.ts +8 -0
  29. package/src/backend/routes/activitypub/inbox.ts +410 -205
  30. package/src/backend/routes/activitypub/outbox.ts +0 -0
  31. package/src/backend/routes/auth.ts +2 -1
  32. package/src/backend/routes/communities/messages.ts +53 -17
  33. package/src/backend/routes/dm/messages.ts +47 -7
  34. package/src/backend/routes/dm/read-archive.ts +27 -0
  35. package/src/backend/routes/dm/typing.ts +16 -0
  36. package/src/backend/routes/notifications.ts +25 -0
  37. package/src/backend/routes/posts/post-helpers.ts +42 -23
  38. package/src/backend/routes/realtime/index.ts +67 -0
  39. package/src/backend/routes/rtc/index.ts +5 -1
  40. package/src/backend/runtime/call-hub-core.ts +13 -3
  41. package/src/backend/runtime/cloudflare.ts +63 -2
  42. package/src/backend/runtime/managed-relational.ts +197 -0
  43. package/src/backend/runtime/managed-runtime.ts +631 -0
  44. package/src/backend/runtime/queue.ts +40 -0
  45. package/src/backend/runtime/realtime-hub.ts +257 -0
  46. package/src/backend/runtime/realtime-stream-do.ts +323 -0
  47. package/src/backend/server.ts +15 -18
  48. package/src/backend/types.ts +13 -2
  49. package/src/db/d1-write.ts +270 -0
  50. package/src/db/index.ts +17 -0
  51. package/src/db/schema/federation.ts +19 -0
  52. package/src/db/schema/index.ts +1 -0
@@ -0,0 +1,197 @@
1
+ import {
2
+ TAKOSUMI_MANAGED_RELATIONAL_RUNTIME_CONTRACT,
3
+ managedRelationalBatchGatewayRequest,
4
+ managedRelationalConnection,
5
+ parseManagedRelationalBatchResponse,
6
+ type ManagedRelationalMethod,
7
+ type ManagedRelationalParameter,
8
+ } from "@takosjp/takosumi-contract/managed-relational-runtime";
9
+ import { parseManagedRuntimeConnectionMaterialization } from "@takosjp/takosumi-contract/managed-runtime-connections";
10
+ import {
11
+ drizzle as drizzleProxy,
12
+ type AsyncBatchRemoteCallback,
13
+ type AsyncRemoteCallback,
14
+ } from "drizzle-orm/sqlite-proxy";
15
+
16
+ import * as schema from "../../db/schema.ts";
17
+ import { ManagedRuntimeGatewayError } from "./managed-runtime.ts";
18
+ import type { ManagedRuntimeGateway } from "./managed-runtime.ts";
19
+
20
+ const DEFAULT_MAX_RELATIONAL_RESPONSE_BYTES = 8 * 1024 * 1024;
21
+
22
+ export interface ManagedRelationalDatabaseOptions {
23
+ readonly materialization: unknown;
24
+ readonly gateway: ManagedRuntimeGateway;
25
+ readonly alias: string;
26
+ readonly idempotencyKey?: () => string;
27
+ readonly maxResponseBytes?: number;
28
+ }
29
+
30
+ /**
31
+ * Provider-neutral Drizzle adapter for a host-issued RelationalDatabase.
32
+ *
33
+ * Every callback is one bounded prepared statement. Drizzle `batch()` maps to
34
+ * one ordered-atomic host call; transaction-control and migration SQL are
35
+ * deliberately unavailable on this request path.
36
+ */
37
+ export function createManagedRelationalDatabase(
38
+ options: ManagedRelationalDatabaseOptions,
39
+ ) {
40
+ const materialization = parseManagedRuntimeConnectionMaterialization(
41
+ options.materialization,
42
+ );
43
+ const connection = managedRelationalConnection(
44
+ materialization,
45
+ options.alias,
46
+ );
47
+ const idempotencyKey =
48
+ options.idempotencyKey ??
49
+ (() => `yurucommu.relational:${crypto.randomUUID()}`);
50
+ const maxResponseBytes =
51
+ options.maxResponseBytes ?? DEFAULT_MAX_RELATIONAL_RESPONSE_BYTES;
52
+ if (
53
+ !Number.isSafeInteger(maxResponseBytes) ||
54
+ maxResponseBytes < 1 ||
55
+ maxResponseBytes > DEFAULT_MAX_RELATIONAL_RESPONSE_BYTES
56
+ ) {
57
+ throw new TypeError("managed_relational_response_limit_invalid");
58
+ }
59
+
60
+ const execute = async (
61
+ statements: readonly {
62
+ readonly sql: string;
63
+ readonly params: readonly unknown[];
64
+ readonly method: ManagedRelationalMethod;
65
+ }[],
66
+ ) => {
67
+ const canonical = statements.map((statement) => ({
68
+ sql: statement.sql,
69
+ params: statement.params.map(relationalParameter),
70
+ method: statement.method,
71
+ }));
72
+ const request = managedRelationalBatchGatewayRequest(connection.authority, {
73
+ statements: canonical,
74
+ idempotencyKey: idempotencyKey(),
75
+ });
76
+ const response = await boundedResponse(
77
+ await options.gateway.fetch(request),
78
+ maxResponseBytes,
79
+ );
80
+ if (!response.ok) {
81
+ const value = (await response.json().catch(() => undefined)) as unknown;
82
+ const code =
83
+ isRecord(value) && typeof value.error === "string"
84
+ ? value.error
85
+ : "managed_relational_request_failed";
86
+ throw new ManagedRuntimeGatewayError(
87
+ code,
88
+ response.status,
89
+ response.status === 429 || response.status >= 500,
90
+ );
91
+ }
92
+ const value = parseManagedRelationalBatchResponse(
93
+ await response.json(),
94
+ canonical.length,
95
+ );
96
+ return value.results;
97
+ };
98
+
99
+ const callback: AsyncRemoteCallback = async (sql, params, method) => {
100
+ const [result] = await execute([{ sql, params, method }]);
101
+ if (!result) throw new Error("managed_relational_result_missing");
102
+ return drizzleResult(result, method);
103
+ };
104
+ const batchCallback: AsyncBatchRemoteCallback = async (batch) => {
105
+ const results = await execute(batch);
106
+ return results.map((result, index) =>
107
+ drizzleResult(result, batch[index]!.method),
108
+ );
109
+ };
110
+
111
+ return drizzleProxy(callback, batchCallback, { schema });
112
+ }
113
+
114
+ function drizzleResult(
115
+ result: Awaited<
116
+ ReturnType<typeof parseManagedRelationalBatchResponse>
117
+ >["results"][number],
118
+ method: ManagedRelationalMethod,
119
+ ) {
120
+ return {
121
+ // sqlite-proxy exposes mutable `any[]` at this boundary even though the
122
+ // public runtime contract is intentionally immutable. Copy here so the
123
+ // provider-neutral contract never leaks a mutable result reference.
124
+ rows:
125
+ method === "get"
126
+ ? [...(result.rows[0] ?? [])]
127
+ : result.rows.map((row) => [...row]),
128
+ meta: result.meta,
129
+ };
130
+ }
131
+
132
+ function relationalParameter(value: unknown): ManagedRelationalParameter {
133
+ if (value === null || typeof value === "string") return value;
134
+ if (typeof value === "number" && Number.isFinite(value)) return value;
135
+ if (typeof value === "boolean") return value ? 1 : 0;
136
+ throw new TypeError("managed_relational_parameter_unsupported");
137
+ }
138
+
139
+ async function boundedResponse(
140
+ response: Response,
141
+ maxBytes: number,
142
+ ): Promise<Response> {
143
+ const declaredLength = response.headers.get("content-length");
144
+ if (
145
+ declaredLength !== null &&
146
+ (!/^\d+$/u.test(declaredLength) || Number(declaredLength) > maxBytes)
147
+ ) {
148
+ throw new ManagedRuntimeGatewayError(
149
+ "managed_relational_response_too_large",
150
+ 502,
151
+ false,
152
+ );
153
+ }
154
+ const reader = response.body?.getReader();
155
+ if (!reader) {
156
+ return new Response(null, {
157
+ status: response.status,
158
+ statusText: response.statusText,
159
+ headers: response.headers,
160
+ });
161
+ }
162
+ const chunks: Uint8Array[] = [];
163
+ let size = 0;
164
+ try {
165
+ while (true) {
166
+ const { done, value } = await reader.read();
167
+ if (done) break;
168
+ size += value.byteLength;
169
+ if (size > maxBytes) {
170
+ await reader.cancel("managed_relational_response_too_large");
171
+ throw new ManagedRuntimeGatewayError(
172
+ "managed_relational_response_too_large",
173
+ 502,
174
+ false,
175
+ );
176
+ }
177
+ chunks.push(value);
178
+ }
179
+ } finally {
180
+ reader.releaseLock();
181
+ }
182
+ const body = new Uint8Array(size);
183
+ let offset = 0;
184
+ for (const chunk of chunks) {
185
+ body.set(chunk, offset);
186
+ offset += chunk.byteLength;
187
+ }
188
+ return new Response(body, {
189
+ status: response.status,
190
+ statusText: response.statusText,
191
+ headers: response.headers,
192
+ });
193
+ }
194
+
195
+ function isRecord(value: unknown): value is Record<string, unknown> {
196
+ return typeof value === "object" && value !== null && !Array.isArray(value);
197
+ }