@korajs/server 1.0.0-beta.2 → 1.0.0-beta.4
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/index.cjs +173 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -1
- package/dist/index.d.ts +88 -1
- package/dist/index.js +180 -33
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -3277,6 +3277,57 @@ var YjsDocRelay = class {
|
|
|
3277
3277
|
// src/server/route-context.ts
|
|
3278
3278
|
init_cjs_shims();
|
|
3279
3279
|
var import_core12 = require("@korajs/core");
|
|
3280
|
+
|
|
3281
|
+
// src/apply/route-predicate.ts
|
|
3282
|
+
init_cjs_shims();
|
|
3283
|
+
function evaluateField(actual, operators) {
|
|
3284
|
+
if ("$eq" in operators && !Object.is(actual, operators.$eq)) {
|
|
3285
|
+
return false;
|
|
3286
|
+
}
|
|
3287
|
+
if ("$ne" in operators && Object.is(actual, operators.$ne)) {
|
|
3288
|
+
return false;
|
|
3289
|
+
}
|
|
3290
|
+
if (operators.$in !== void 0 && !operators.$in.some((candidate) => Object.is(actual, candidate))) {
|
|
3291
|
+
return false;
|
|
3292
|
+
}
|
|
3293
|
+
const numericChecks = [
|
|
3294
|
+
["$lt", (a, b) => a < b],
|
|
3295
|
+
["$lte", (a, b) => a <= b],
|
|
3296
|
+
["$gt", (a, b) => a > b],
|
|
3297
|
+
["$gte", (a, b) => a >= b]
|
|
3298
|
+
];
|
|
3299
|
+
for (const [operator, compare] of numericChecks) {
|
|
3300
|
+
const bound = operators[operator];
|
|
3301
|
+
if (bound === void 0) {
|
|
3302
|
+
continue;
|
|
3303
|
+
}
|
|
3304
|
+
if (typeof actual !== "number" || typeof bound !== "number") {
|
|
3305
|
+
return false;
|
|
3306
|
+
}
|
|
3307
|
+
if (!compare(actual, bound)) {
|
|
3308
|
+
return false;
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
return true;
|
|
3312
|
+
}
|
|
3313
|
+
function evaluateRoutePredicate(record, predicate) {
|
|
3314
|
+
const fields = Object.keys(predicate);
|
|
3315
|
+
if (fields.length === 0) {
|
|
3316
|
+
return true;
|
|
3317
|
+
}
|
|
3318
|
+
if (record === null) {
|
|
3319
|
+
return false;
|
|
3320
|
+
}
|
|
3321
|
+
for (const field of fields) {
|
|
3322
|
+
const operators = predicate[field];
|
|
3323
|
+
if (operators && !evaluateField(record[field], operators)) {
|
|
3324
|
+
return false;
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
return true;
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
// src/server/route-context.ts
|
|
3280
3331
|
function recordMatchesScope(collection, record, scope) {
|
|
3281
3332
|
const collectionScope = scope[collection];
|
|
3282
3333
|
if (!collectionScope) {
|
|
@@ -3327,7 +3378,18 @@ async function buildRouteOperation(store, mutation) {
|
|
|
3327
3378
|
}
|
|
3328
3379
|
const current = await store.findRecord(mutation.collection, mutation.recordId);
|
|
3329
3380
|
if (mutation.type === "update") {
|
|
3330
|
-
const
|
|
3381
|
+
const rawData = mutation.data ?? {};
|
|
3382
|
+
const data = {};
|
|
3383
|
+
const atomicOps = {};
|
|
3384
|
+
for (const key of Object.keys(rawData)) {
|
|
3385
|
+
const value = rawData[key];
|
|
3386
|
+
if ((0, import_core12.isAtomicOp)(value)) {
|
|
3387
|
+
data[key] = (0, import_core12.resolveAtomicOp)(current ? current[key] : void 0, value);
|
|
3388
|
+
atomicOps[key] = (0, import_core12.toAtomicOp)(value);
|
|
3389
|
+
} else {
|
|
3390
|
+
data[key] = value;
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3331
3393
|
return (0, import_core12.createOperation)(
|
|
3332
3394
|
{
|
|
3333
3395
|
nodeId,
|
|
@@ -3335,10 +3397,11 @@ async function buildRouteOperation(store, mutation) {
|
|
|
3335
3397
|
collection: mutation.collection,
|
|
3336
3398
|
recordId: mutation.recordId,
|
|
3337
3399
|
data,
|
|
3338
|
-
previousData: current ? pickFields(current, Object.keys(
|
|
3400
|
+
previousData: current ? pickFields(current, Object.keys(rawData)) : {},
|
|
3339
3401
|
sequenceNumber,
|
|
3340
3402
|
causalDeps: [],
|
|
3341
|
-
schemaVersion
|
|
3403
|
+
schemaVersion,
|
|
3404
|
+
...Object.keys(atomicOps).length > 0 ? { atomicOps } : {}
|
|
3342
3405
|
},
|
|
3343
3406
|
clock
|
|
3344
3407
|
);
|
|
@@ -3376,45 +3439,123 @@ function createRouteContext(server, store) {
|
|
|
3376
3439
|
);
|
|
3377
3440
|
return run;
|
|
3378
3441
|
}
|
|
3442
|
+
async function prepareMutation(mutation, scope) {
|
|
3443
|
+
let op;
|
|
3444
|
+
try {
|
|
3445
|
+
op = await buildRouteOperation(store, mutation);
|
|
3446
|
+
} catch (error) {
|
|
3447
|
+
if (error instanceof RouteMutationError) {
|
|
3448
|
+
return {
|
|
3449
|
+
ok: false,
|
|
3450
|
+
rejection: {
|
|
3451
|
+
ok: false,
|
|
3452
|
+
code: error.code,
|
|
3453
|
+
message: error.message,
|
|
3454
|
+
retriable: isRetriableRejection(error.code)
|
|
3455
|
+
}
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
throw error;
|
|
3459
|
+
}
|
|
3460
|
+
if (scope && !operationMatchesScopes(op, scope)) {
|
|
3461
|
+
return {
|
|
3462
|
+
ok: false,
|
|
3463
|
+
rejection: {
|
|
3464
|
+
ok: false,
|
|
3465
|
+
code: "SCOPE_VIOLATION",
|
|
3466
|
+
message: `Mutation on "${mutation.collection}" is outside the provided scope.`,
|
|
3467
|
+
retriable: isRetriableRejection("SCOPE_VIOLATION")
|
|
3468
|
+
}
|
|
3469
|
+
};
|
|
3470
|
+
}
|
|
3471
|
+
return { ok: true, op };
|
|
3472
|
+
}
|
|
3473
|
+
async function commitOperation(op, collection) {
|
|
3474
|
+
const result = await server.applyLocalOperation(op);
|
|
3475
|
+
if (result.result !== "applied") {
|
|
3476
|
+
const code = result.rejection?.code ?? "NOT_APPLIED";
|
|
3477
|
+
return {
|
|
3478
|
+
ok: false,
|
|
3479
|
+
code,
|
|
3480
|
+
message: result.rejection?.message ?? `Operation on "${collection}" was not applied (${result.result}).`,
|
|
3481
|
+
// Prefer the pipeline's own classification when present; fall back
|
|
3482
|
+
// to classifying the code so this stays correct if new codes appear.
|
|
3483
|
+
retriable: result.rejection?.retriable ?? isRetriableRejection(code)
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
const record = await store.findRecord(collection, op.recordId);
|
|
3487
|
+
return { ok: true, operation: op, record };
|
|
3488
|
+
}
|
|
3489
|
+
async function rawApply(mutation, scope) {
|
|
3490
|
+
const prepared = await prepareMutation(mutation, scope);
|
|
3491
|
+
if (!prepared.ok) {
|
|
3492
|
+
return prepared.rejection;
|
|
3493
|
+
}
|
|
3494
|
+
return commitOperation(prepared.op, mutation.collection);
|
|
3495
|
+
}
|
|
3379
3496
|
return {
|
|
3380
3497
|
apply(mutation, options) {
|
|
3498
|
+
return serialize(() => rawApply(mutation, options?.scope));
|
|
3499
|
+
},
|
|
3500
|
+
applyConditional(spec, options) {
|
|
3381
3501
|
return serialize(async () => {
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3502
|
+
if (spec.idempotencyKey) {
|
|
3503
|
+
const existing = await store.findRecord(
|
|
3504
|
+
spec.idempotencyKey.collection,
|
|
3505
|
+
spec.idempotencyKey.id
|
|
3506
|
+
);
|
|
3507
|
+
if (existing) {
|
|
3508
|
+
return { ok: true, idempotent: true, operations: [], records: [existing] };
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
if (spec.if && Object.keys(spec.if).length > 0) {
|
|
3512
|
+
const target = await store.findRecord(spec.collection, spec.id);
|
|
3513
|
+
if (!evaluateRoutePredicate(target, spec.if)) {
|
|
3514
|
+
const code = spec.reject?.code ?? "CONDITION_NOT_MET";
|
|
3387
3515
|
return {
|
|
3388
3516
|
ok: false,
|
|
3389
|
-
code
|
|
3390
|
-
message:
|
|
3391
|
-
retriable:
|
|
3517
|
+
code,
|
|
3518
|
+
message: spec.reject?.message ?? `Admission condition not met on "${spec.collection}" record "${spec.id}".`,
|
|
3519
|
+
retriable: false
|
|
3392
3520
|
};
|
|
3393
3521
|
}
|
|
3394
|
-
throw error;
|
|
3395
3522
|
}
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3523
|
+
const mutations = [];
|
|
3524
|
+
if (spec.update !== void 0 && spec.update !== null) {
|
|
3525
|
+
mutations.push({
|
|
3526
|
+
collection: spec.collection,
|
|
3527
|
+
type: "update",
|
|
3528
|
+
recordId: spec.id,
|
|
3529
|
+
data: spec.update
|
|
3530
|
+
});
|
|
3403
3531
|
}
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3532
|
+
if (spec.also) {
|
|
3533
|
+
mutations.push(...spec.also);
|
|
3534
|
+
}
|
|
3535
|
+
const prepared = [];
|
|
3536
|
+
for (const mutation of mutations) {
|
|
3537
|
+
const p = await prepareMutation(mutation, options?.scope);
|
|
3538
|
+
if (!p.ok) {
|
|
3539
|
+
return p.rejection;
|
|
3540
|
+
}
|
|
3541
|
+
prepared.push({ op: p.op, collection: mutation.collection });
|
|
3542
|
+
}
|
|
3543
|
+
const operations2 = [];
|
|
3544
|
+
const records = [];
|
|
3545
|
+
for (const { op, collection } of prepared) {
|
|
3546
|
+
const result = await commitOperation(op, collection);
|
|
3547
|
+
if (!result.ok) {
|
|
3548
|
+
return {
|
|
3549
|
+
ok: false,
|
|
3550
|
+
code: result.code,
|
|
3551
|
+
message: result.message,
|
|
3552
|
+
retriable: result.retriable
|
|
3553
|
+
};
|
|
3554
|
+
}
|
|
3555
|
+
operations2.push(result.operation);
|
|
3556
|
+
records.push(result.record);
|
|
3415
3557
|
}
|
|
3416
|
-
|
|
3417
|
-
return { ok: true, operation: op, record };
|
|
3558
|
+
return { ok: true, idempotent: false, operations: operations2, records };
|
|
3418
3559
|
});
|
|
3419
3560
|
},
|
|
3420
3561
|
async query(collection, options) {
|