@korajs/server 0.6.0 → 1.0.0-beta.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/LICENSE +21 -0
- package/dist/index.cjs +96 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +61 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -58,6 +58,7 @@ function createDefaultLogger() {
|
|
|
58
58
|
import { generateUUIDv7 } from "@korajs/core";
|
|
59
59
|
|
|
60
60
|
// src/store/materialization.ts
|
|
61
|
+
import { StorageError } from "@korajs/core";
|
|
61
62
|
function fieldTypeToSql(descriptor, dialect) {
|
|
62
63
|
switch (descriptor.kind) {
|
|
63
64
|
case "string":
|
|
@@ -153,6 +154,52 @@ function replayOperationsForRecord(ops) {
|
|
|
153
154
|
}
|
|
154
155
|
return deleted ? null : record;
|
|
155
156
|
}
|
|
157
|
+
var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
158
|
+
var BASE64_REVERSE = new Map(
|
|
159
|
+
Array.from(BASE64_ALPHABET, (char, index3) => [char, index3])
|
|
160
|
+
);
|
|
161
|
+
function base64ToBytes(base64) {
|
|
162
|
+
const cleaned = base64.replace(/=+$/, "");
|
|
163
|
+
const out = new Uint8Array(Math.floor(cleaned.length * 6 / 8));
|
|
164
|
+
let buffer = 0;
|
|
165
|
+
let bits = 0;
|
|
166
|
+
let index3 = 0;
|
|
167
|
+
for (const char of cleaned) {
|
|
168
|
+
const value = BASE64_REVERSE.get(char);
|
|
169
|
+
if (value === void 0) {
|
|
170
|
+
throw new StorageError(`Invalid base64 character "${char}" in tagged richtext value`, {
|
|
171
|
+
char
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
buffer = buffer << 6 | value;
|
|
175
|
+
bits += 6;
|
|
176
|
+
if (bits >= 8) {
|
|
177
|
+
bits -= 8;
|
|
178
|
+
out[index3] = buffer >> bits & 255;
|
|
179
|
+
index3 += 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
function decodeRichtextColumnValue(value) {
|
|
185
|
+
if (typeof value !== "object" || value === null || ArrayBuffer.isView(value)) {
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
const record = value;
|
|
189
|
+
if (Object.keys(record).length === 1 && typeof record.$koraBytes === "string") {
|
|
190
|
+
return base64ToBytes(record.$koraBytes);
|
|
191
|
+
}
|
|
192
|
+
const keys = Object.keys(record);
|
|
193
|
+
if (keys.length > 0 && keys.every((k, i) => k === String(i) && typeof record[k] === "number")) {
|
|
194
|
+
const bytes = new Uint8Array(keys.length);
|
|
195
|
+
for (let i = 0; i < keys.length; i++) {
|
|
196
|
+
const byte = record[String(i)];
|
|
197
|
+
bytes[i] = typeof byte === "number" ? byte & 255 : 0;
|
|
198
|
+
}
|
|
199
|
+
return bytes;
|
|
200
|
+
}
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
156
203
|
function serializeFieldValue(value, descriptor) {
|
|
157
204
|
if (value === null || value === void 0) return null;
|
|
158
205
|
switch (descriptor.kind) {
|
|
@@ -160,6 +207,8 @@ function serializeFieldValue(value, descriptor) {
|
|
|
160
207
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
161
208
|
case "boolean":
|
|
162
209
|
return value ? 1 : 0;
|
|
210
|
+
case "richtext":
|
|
211
|
+
return decodeRichtextColumnValue(value);
|
|
163
212
|
default:
|
|
164
213
|
return value;
|
|
165
214
|
}
|
|
@@ -2060,9 +2109,17 @@ function asRecord(value) {
|
|
|
2060
2109
|
}
|
|
2061
2110
|
|
|
2062
2111
|
// src/session/operation-validation.ts
|
|
2112
|
+
import { MAX_LOGICAL } from "@korajs/core";
|
|
2063
2113
|
var SERVER_MAX_TIMESTAMP_FUTURE_MS = 6e4;
|
|
2064
2114
|
function isOperationTimestampValid(op, now = Date.now()) {
|
|
2065
|
-
|
|
2115
|
+
const { wallTime, logical } = op.timestamp;
|
|
2116
|
+
if (!Number.isInteger(wallTime) || wallTime < 0) {
|
|
2117
|
+
return false;
|
|
2118
|
+
}
|
|
2119
|
+
if (!Number.isInteger(logical) || logical < 0 || logical > MAX_LOGICAL) {
|
|
2120
|
+
return false;
|
|
2121
|
+
}
|
|
2122
|
+
return wallTime <= now + SERVER_MAX_TIMESTAMP_FUTURE_MS;
|
|
2066
2123
|
}
|
|
2067
2124
|
|
|
2068
2125
|
// src/session/session-operation-limits.ts
|
|
@@ -2310,7 +2367,8 @@ var ClientSession = class {
|
|
|
2310
2367
|
accepted: false,
|
|
2311
2368
|
rejectReason: `${SCHEMA_MISMATCH_PREFIX}: client schema version ${msg.schemaVersion} not in supported range [${min}, ${max}]`,
|
|
2312
2369
|
supportedSchemaMin: min,
|
|
2313
|
-
supportedSchemaMax: max
|
|
2370
|
+
supportedSchemaMax: max,
|
|
2371
|
+
serverTime: Date.now()
|
|
2314
2372
|
};
|
|
2315
2373
|
this.sendToClient(response2);
|
|
2316
2374
|
this.close("schema version mismatch");
|
|
@@ -2324,6 +2382,7 @@ var ClientSession = class {
|
|
|
2324
2382
|
schemaVersion: this.schemaVersion,
|
|
2325
2383
|
accepted: true,
|
|
2326
2384
|
selectedWireFormat,
|
|
2385
|
+
serverTime: Date.now(),
|
|
2327
2386
|
// Confirm the accepted scope so the client knows what data will be synced.
|
|
2328
2387
|
// This may differ from what the client requested if auth scopes are narrower.
|
|
2329
2388
|
...this.authContext?.scopes ? { acceptedScope: this.authContext.scopes } : {}
|