@korajs/server 1.0.0-beta.10 → 1.0.0-beta.11
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 +66 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +62 -4
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -35,6 +35,18 @@ type RoutePredicate = Record<string, RoutePredicateOperators>;
|
|
|
35
35
|
* Per-collection scope map from auth context.
|
|
36
36
|
*/
|
|
37
37
|
type ScopeMap = Record<string, Record<string, unknown>>;
|
|
38
|
+
declare const DEFAULT_MAX_SCOPE_PREDICATE_VALUES = 100;
|
|
39
|
+
/** Canonicalize bounded `$in` predicates so equivalent authorization has one signature. */
|
|
40
|
+
declare function normalizeScopeMap(scopes: ScopeMap, maxValues?: number): ScopeMap;
|
|
41
|
+
/**
|
|
42
|
+
* Returns true if an operation is visible to a session based on its scopes.
|
|
43
|
+
*
|
|
44
|
+
* Rules:
|
|
45
|
+
* - No scopes configured => visible
|
|
46
|
+
* - Collection missing from scope map => hidden
|
|
47
|
+
* - All scoped field/value pairs must match the operation snapshot
|
|
48
|
+
*/
|
|
49
|
+
declare function operationMatchesScopes(op: Operation, scopes: ScopeMap | undefined, fullRecord?: Record<string, unknown> | null): boolean;
|
|
38
50
|
|
|
39
51
|
/**
|
|
40
52
|
* A materialized record reconstructed from the operation log
|
|
@@ -2011,4 +2023,4 @@ declare class AwarenessRelay {
|
|
|
2011
2023
|
*/
|
|
2012
2024
|
declare function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer;
|
|
2013
2025
|
|
|
2014
|
-
export { type AuthContext, type AuthProvider, AwarenessRelay, type AwarenessRelayCallback, type ClientMetrics, ClientSession, type ClientSessionOptions, type CollectionQueryOptions, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraAuthProvider, type KoraAuthProviderOptions, KoraSyncServer, type KoraSyncServerConfig, type LogEntry, type LogLevel, type Logger, type MaterializedRecord, MemoryServerStore, MixedAuthProvider, type MixedAuthProviderOptions, NoAuthProvider, type OperationDecision, type OperationRejection, type OperationValidationContext, type OperationValidator, PostgresServerStore, type ProductionHttpRoute, type ProductionHttpRouteContext, type ProductionHttpRouteRequest, type ProductionHttpRouteResponse, type ProductionServer, type ProductionServerConfig, RETRIABLE_REJECTION_CODES, type RelayCallback, type RouteApplyResult, type RouteConditionalApply, type RouteConditionalResult, type RouteMutation, type RoutePredicate, type RoutePredicateOperators, type RouteScopeOptions, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, ServerMetricsCollector, type ServerMetricsSnapshot, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createDefaultLogger, createJsonLogger, createKoraServer, createPostgresServerStore, createPrettyLogger, createProductionServer, createSilentLogger, createSqliteServerStore, isRetriableRejection };
|
|
2026
|
+
export { type AuthContext, type AuthProvider, AwarenessRelay, type AwarenessRelayCallback, type ClientMetrics, ClientSession, type ClientSessionOptions, type CollectionQueryOptions, DEFAULT_MAX_SCOPE_PREDICATE_VALUES, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraAuthProvider, type KoraAuthProviderOptions, KoraSyncServer, type KoraSyncServerConfig, type LogEntry, type LogLevel, type Logger, type MaterializedRecord, MemoryServerStore, MixedAuthProvider, type MixedAuthProviderOptions, NoAuthProvider, type OperationDecision, type OperationRejection, type OperationValidationContext, type OperationValidator, PostgresServerStore, type ProductionHttpRoute, type ProductionHttpRouteContext, type ProductionHttpRouteRequest, type ProductionHttpRouteResponse, type ProductionServer, type ProductionServerConfig, RETRIABLE_REJECTION_CODES, type RelayCallback, type RouteApplyResult, type RouteConditionalApply, type RouteConditionalResult, type RouteMutation, type RoutePredicate, type RoutePredicateOperators, type RouteScopeOptions, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, ServerMetricsCollector, type ServerMetricsSnapshot, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createDefaultLogger, createJsonLogger, createKoraServer, createPostgresServerStore, createPrettyLogger, createProductionServer, createSilentLogger, createSqliteServerStore, isRetriableRejection, normalizeScopeMap, operationMatchesScopes };
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,18 @@ type RoutePredicate = Record<string, RoutePredicateOperators>;
|
|
|
35
35
|
* Per-collection scope map from auth context.
|
|
36
36
|
*/
|
|
37
37
|
type ScopeMap = Record<string, Record<string, unknown>>;
|
|
38
|
+
declare const DEFAULT_MAX_SCOPE_PREDICATE_VALUES = 100;
|
|
39
|
+
/** Canonicalize bounded `$in` predicates so equivalent authorization has one signature. */
|
|
40
|
+
declare function normalizeScopeMap(scopes: ScopeMap, maxValues?: number): ScopeMap;
|
|
41
|
+
/**
|
|
42
|
+
* Returns true if an operation is visible to a session based on its scopes.
|
|
43
|
+
*
|
|
44
|
+
* Rules:
|
|
45
|
+
* - No scopes configured => visible
|
|
46
|
+
* - Collection missing from scope map => hidden
|
|
47
|
+
* - All scoped field/value pairs must match the operation snapshot
|
|
48
|
+
*/
|
|
49
|
+
declare function operationMatchesScopes(op: Operation, scopes: ScopeMap | undefined, fullRecord?: Record<string, unknown> | null): boolean;
|
|
38
50
|
|
|
39
51
|
/**
|
|
40
52
|
* A materialized record reconstructed from the operation log
|
|
@@ -2011,4 +2023,4 @@ declare class AwarenessRelay {
|
|
|
2011
2023
|
*/
|
|
2012
2024
|
declare function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer;
|
|
2013
2025
|
|
|
2014
|
-
export { type AuthContext, type AuthProvider, AwarenessRelay, type AwarenessRelayCallback, type ClientMetrics, ClientSession, type ClientSessionOptions, type CollectionQueryOptions, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraAuthProvider, type KoraAuthProviderOptions, KoraSyncServer, type KoraSyncServerConfig, type LogEntry, type LogLevel, type Logger, type MaterializedRecord, MemoryServerStore, MixedAuthProvider, type MixedAuthProviderOptions, NoAuthProvider, type OperationDecision, type OperationRejection, type OperationValidationContext, type OperationValidator, PostgresServerStore, type ProductionHttpRoute, type ProductionHttpRouteContext, type ProductionHttpRouteRequest, type ProductionHttpRouteResponse, type ProductionServer, type ProductionServerConfig, RETRIABLE_REJECTION_CODES, type RelayCallback, type RouteApplyResult, type RouteConditionalApply, type RouteConditionalResult, type RouteMutation, type RoutePredicate, type RoutePredicateOperators, type RouteScopeOptions, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, ServerMetricsCollector, type ServerMetricsSnapshot, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createDefaultLogger, createJsonLogger, createKoraServer, createPostgresServerStore, createPrettyLogger, createProductionServer, createSilentLogger, createSqliteServerStore, isRetriableRejection };
|
|
2026
|
+
export { type AuthContext, type AuthProvider, AwarenessRelay, type AwarenessRelayCallback, type ClientMetrics, ClientSession, type ClientSessionOptions, type CollectionQueryOptions, DEFAULT_MAX_SCOPE_PREDICATE_VALUES, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraAuthProvider, type KoraAuthProviderOptions, KoraSyncServer, type KoraSyncServerConfig, type LogEntry, type LogLevel, type Logger, type MaterializedRecord, MemoryServerStore, MixedAuthProvider, type MixedAuthProviderOptions, NoAuthProvider, type OperationDecision, type OperationRejection, type OperationValidationContext, type OperationValidator, PostgresServerStore, type ProductionHttpRoute, type ProductionHttpRouteContext, type ProductionHttpRouteRequest, type ProductionHttpRouteResponse, type ProductionServer, type ProductionServerConfig, RETRIABLE_REJECTION_CODES, type RelayCallback, type RouteApplyResult, type RouteConditionalApply, type RouteConditionalResult, type RouteMutation, type RoutePredicate, type RoutePredicateOperators, type RouteScopeOptions, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, ServerMetricsCollector, type ServerMetricsSnapshot, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createDefaultLogger, createJsonLogger, createKoraServer, createPostgresServerStore, createPrettyLogger, createProductionServer, createSilentLogger, createSqliteServerStore, isRetriableRejection, normalizeScopeMap, operationMatchesScopes };
|
package/dist/index.js
CHANGED
|
@@ -2523,6 +2523,39 @@ function mergeScopeMaps(base, overlay, source) {
|
|
|
2523
2523
|
}
|
|
2524
2524
|
|
|
2525
2525
|
// src/scopes/server-scope-filter.ts
|
|
2526
|
+
var DEFAULT_MAX_SCOPE_PREDICATE_VALUES = 100;
|
|
2527
|
+
function normalizeScopeMap(scopes, maxValues = DEFAULT_MAX_SCOPE_PREDICATE_VALUES) {
|
|
2528
|
+
const normalized = {};
|
|
2529
|
+
for (const collection of Object.keys(scopes).sort()) {
|
|
2530
|
+
const predicate = {};
|
|
2531
|
+
for (const field of Object.keys(scopes[collection] ?? {}).sort()) {
|
|
2532
|
+
const expected = scopes[collection]?.[field];
|
|
2533
|
+
if (expected && typeof expected === "object" && !Array.isArray(expected) && "$in" in expected) {
|
|
2534
|
+
const values = expected.$in;
|
|
2535
|
+
if (!Array.isArray(values))
|
|
2536
|
+
throw new Error(`Invalid $in predicate for ${collection}.${field}`);
|
|
2537
|
+
const unique = [...new Map(values.map((value) => [stableValueKey(value), value])).values()];
|
|
2538
|
+
if (unique.length > maxValues)
|
|
2539
|
+
throw new Error(
|
|
2540
|
+
`Scope predicate for ${collection}.${field} exceeds the ${maxValues}-value limit`
|
|
2541
|
+
);
|
|
2542
|
+
predicate[field] = {
|
|
2543
|
+
$in: unique.sort((a, b) => stableValueKey(a).localeCompare(stableValueKey(b)))
|
|
2544
|
+
};
|
|
2545
|
+
} else predicate[field] = expected;
|
|
2546
|
+
}
|
|
2547
|
+
normalized[collection] = predicate;
|
|
2548
|
+
}
|
|
2549
|
+
return normalized;
|
|
2550
|
+
}
|
|
2551
|
+
function stableValueKey(value) {
|
|
2552
|
+
if (Array.isArray(value)) return `[${value.map(stableValueKey).join(",")}]`;
|
|
2553
|
+
if (value && typeof value === "object")
|
|
2554
|
+
return `{${Object.keys(value).sort().map(
|
|
2555
|
+
(key) => `${JSON.stringify(key)}:${stableValueKey(value[key])}`
|
|
2556
|
+
).join(",")}}`;
|
|
2557
|
+
return JSON.stringify(value);
|
|
2558
|
+
}
|
|
2526
2559
|
function operationMatchesScopes(op, scopes, fullRecord) {
|
|
2527
2560
|
if (!scopes) return true;
|
|
2528
2561
|
const collectionScope = scopes[op.collection];
|
|
@@ -2531,12 +2564,19 @@ function operationMatchesScopes(op, scopes, fullRecord) {
|
|
|
2531
2564
|
const snapshot = buildSnapshot(op, fullRecord);
|
|
2532
2565
|
if (!snapshot) return false;
|
|
2533
2566
|
for (const [field, expected] of Object.entries(collectionScope)) {
|
|
2534
|
-
if (snapshot[field]
|
|
2567
|
+
if (!matchesPredicate(snapshot[field], expected)) {
|
|
2535
2568
|
return false;
|
|
2536
2569
|
}
|
|
2537
2570
|
}
|
|
2538
2571
|
return true;
|
|
2539
2572
|
}
|
|
2573
|
+
function matchesPredicate(actual, expected) {
|
|
2574
|
+
if (expected && typeof expected === "object" && !Array.isArray(expected) && "$in" in expected) {
|
|
2575
|
+
const values = expected.$in;
|
|
2576
|
+
return Array.isArray(values) && values.some((value) => Object.is(actual, value));
|
|
2577
|
+
}
|
|
2578
|
+
return Object.is(actual, expected);
|
|
2579
|
+
}
|
|
2540
2580
|
function missingScopeFields(op, scopes) {
|
|
2541
2581
|
if (!scopes) return [];
|
|
2542
2582
|
const collectionScope = scopes[op.collection];
|
|
@@ -2653,7 +2693,8 @@ function stableStringify(value) {
|
|
|
2653
2693
|
return JSON.stringify(value);
|
|
2654
2694
|
}
|
|
2655
2695
|
function sameScopeMap(a, b) {
|
|
2656
|
-
|
|
2696
|
+
const normalize = (value) => value && typeof value === "object" ? normalizeScopeMap(value) : value;
|
|
2697
|
+
return stableStringify(normalize(a) ?? null) === stableStringify(normalize(b) ?? null);
|
|
2657
2698
|
}
|
|
2658
2699
|
var ClientSession = class _ClientSession {
|
|
2659
2700
|
state = "connected";
|
|
@@ -3023,10 +3064,24 @@ var ClientSession = class _ClientSession {
|
|
|
3023
3064
|
this.authContext = context;
|
|
3024
3065
|
this.state = "authenticated";
|
|
3025
3066
|
}
|
|
3026
|
-
const
|
|
3067
|
+
const rawResolvedScopes = resolveSessionScopes(this.store.getSchema(), {
|
|
3027
3068
|
handshakeScope: msg.syncScope,
|
|
3028
3069
|
authScopes: this.authContext?.scopes
|
|
3029
3070
|
});
|
|
3071
|
+
let resolvedScopes;
|
|
3072
|
+
try {
|
|
3073
|
+
resolvedScopes = rawResolvedScopes ? normalizeScopeMap(rawResolvedScopes) : void 0;
|
|
3074
|
+
} catch (error) {
|
|
3075
|
+
this.sendToClient({
|
|
3076
|
+
type: "error",
|
|
3077
|
+
messageId: generateUUIDv74(),
|
|
3078
|
+
code: "SCOPE_PREDICATE_LIMIT",
|
|
3079
|
+
message: error instanceof Error ? error.message : "Invalid scope predicate",
|
|
3080
|
+
retriable: false
|
|
3081
|
+
});
|
|
3082
|
+
this.close("invalid scope predicate");
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3030
3085
|
if (resolvedScopes) {
|
|
3031
3086
|
if (this.authContext) {
|
|
3032
3087
|
this.authContext = { ...this.authContext, scopes: resolvedScopes };
|
|
@@ -5519,6 +5574,7 @@ function formatUptime(ms) {
|
|
|
5519
5574
|
export {
|
|
5520
5575
|
AwarenessRelay,
|
|
5521
5576
|
ClientSession,
|
|
5577
|
+
DEFAULT_MAX_SCOPE_PREDICATE_VALUES,
|
|
5522
5578
|
HttpServerTransport,
|
|
5523
5579
|
KoraAuthProvider,
|
|
5524
5580
|
KoraSyncServer,
|
|
@@ -5538,6 +5594,8 @@ export {
|
|
|
5538
5594
|
createProductionServer,
|
|
5539
5595
|
createSilentLogger,
|
|
5540
5596
|
createSqliteServerStore,
|
|
5541
|
-
isRetriableRejection
|
|
5597
|
+
isRetriableRejection,
|
|
5598
|
+
normalizeScopeMap,
|
|
5599
|
+
operationMatchesScopes
|
|
5542
5600
|
};
|
|
5543
5601
|
//# sourceMappingURL=index.js.map
|