@luckystack/core 0.9.2 → 0.10.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.
- package/CHANGELOG.md +24 -1
- package/CLAUDE.md +6 -4
- package/dist/{chunk-VRKJHE7E.js → chunk-5FXBTGYL.js} +13 -1
- package/dist/chunk-5FXBTGYL.js.map +1 -0
- package/dist/client.d.ts +2 -2
- package/dist/client.js +6 -6
- package/dist/client.js.map +1 -1
- package/dist/eslint/index.js +63 -14
- package/dist/eslint/index.js.map +1 -1
- package/dist/index.d.ts +70 -8
- package/dist/index.js +161 -74
- package/dist/index.js.map +1 -1
- package/dist/{pageRouteValidation-bz2LFVQq.d.ts → pageRouteValidation-B4Cmtfjv.d.ts} +9 -1
- package/docs/redis-adapter.md +4 -1
- package/docs/socket-bootstrap.md +25 -2
- package/package.json +1 -1
- package/dist/chunk-VRKJHE7E.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2,11 +2,13 @@ import {
|
|
|
2
2
|
DEFAULT_CSRF_CONFIG,
|
|
3
3
|
DEFAULT_PAGE_ROUTE_RULES,
|
|
4
4
|
INVALID_ERROR_RESPONSE_CODE,
|
|
5
|
+
ROUTED_DATA_QUERY_KEY,
|
|
5
6
|
buildApiResponseEventName,
|
|
6
7
|
buildApiStreamEventName,
|
|
7
8
|
buildGetJoinedRoomsResponseEventName,
|
|
8
9
|
buildJoinRoomResponseEventName,
|
|
9
10
|
buildLeaveRoomResponseEventName,
|
|
11
|
+
buildRoutedGetUrl,
|
|
10
12
|
buildSyncProgressEventName,
|
|
11
13
|
buildSyncResponseEventName,
|
|
12
14
|
clearCsrfToken,
|
|
@@ -49,7 +51,7 @@ import {
|
|
|
49
51
|
tryCatchSync,
|
|
50
52
|
validatePagePath,
|
|
51
53
|
waitForSocket
|
|
52
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-5FXBTGYL.js";
|
|
53
55
|
import {
|
|
54
56
|
getDeployConfig,
|
|
55
57
|
getPortOverride,
|
|
@@ -335,68 +337,6 @@ var applyErrorFormatter = (input) => {
|
|
|
335
337
|
return response;
|
|
336
338
|
};
|
|
337
339
|
|
|
338
|
-
// src/socketTypes.ts
|
|
339
|
-
var _ioInstance = null;
|
|
340
|
-
var setIoInstance = (io) => {
|
|
341
|
-
_ioInstance = io;
|
|
342
|
-
};
|
|
343
|
-
var getIoInstance = () => _ioInstance;
|
|
344
|
-
|
|
345
|
-
// src/runtimeMapsRegistry.ts
|
|
346
|
-
var emptyApi = { apisObject: {}, functionsObject: {} };
|
|
347
|
-
var emptySync = { syncObject: {}, functionsObject: {} };
|
|
348
|
-
var registry3 = createRegistry({
|
|
349
|
-
getRuntimeApiMaps: () => Promise.resolve(emptyApi),
|
|
350
|
-
getRuntimeSyncMaps: () => Promise.resolve(emptySync)
|
|
351
|
-
});
|
|
352
|
-
var registerRuntimeMapsProvider = (provider2) => {
|
|
353
|
-
registry3.register(provider2);
|
|
354
|
-
};
|
|
355
|
-
var isRuntimeMapsProviderRegistered = () => registry3.isRegistered();
|
|
356
|
-
var getRuntimeApiMaps = async () => registry3.get().getRuntimeApiMaps();
|
|
357
|
-
var getRuntimeSyncMaps = async () => registry3.get().getRuntimeSyncMaps();
|
|
358
|
-
|
|
359
|
-
// src/emailRegistry.ts
|
|
360
|
-
var activeSender = null;
|
|
361
|
-
var registry4 = {};
|
|
362
|
-
var registerEmailSender = (sender) => {
|
|
363
|
-
activeSender = sender;
|
|
364
|
-
registry4 = { ...registry4, default: sender };
|
|
365
|
-
};
|
|
366
|
-
var registerEmailSenders = (senders) => {
|
|
367
|
-
registry4 = { ...senders };
|
|
368
|
-
if (senders.default) {
|
|
369
|
-
activeSender = senders.default;
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
var getEmailSender = () => activeSender ?? registry4.default ?? null;
|
|
373
|
-
var getEmailSenderByName = (name) => {
|
|
374
|
-
if (registry4[name]) return registry4[name];
|
|
375
|
-
if (name === "default" && activeSender) return activeSender;
|
|
376
|
-
return null;
|
|
377
|
-
};
|
|
378
|
-
var listEmailSenderNames = () => Object.keys(registry4);
|
|
379
|
-
var isEmailSenderRegistered = () => activeSender !== null || Object.keys(registry4).length > 0;
|
|
380
|
-
|
|
381
|
-
// src/localeReloader.ts
|
|
382
|
-
var registry5 = createRegistry(null);
|
|
383
|
-
var registerLocaleReloader = (reloader) => {
|
|
384
|
-
registry5.register(reloader);
|
|
385
|
-
return reloader;
|
|
386
|
-
};
|
|
387
|
-
var getLocaleReloader = () => registry5.get();
|
|
388
|
-
|
|
389
|
-
// src/errorTrackerIdentity.ts
|
|
390
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
391
|
-
var identityStore = new AsyncLocalStorage();
|
|
392
|
-
var runWithErrorTrackerIdentityScope = (fn) => identityStore.run({ user: null }, fn);
|
|
393
|
-
var runWithErrorTrackerIdentity = (user, fn) => identityStore.run({ user }, fn);
|
|
394
|
-
var setCurrentErrorTrackerIdentity = (user) => {
|
|
395
|
-
const box = identityStore.getStore();
|
|
396
|
-
if (box) box.user = user;
|
|
397
|
-
};
|
|
398
|
-
var getCurrentErrorTrackerIdentity = () => identityStore.getStore()?.user ?? null;
|
|
399
|
-
|
|
400
340
|
// src/env.ts
|
|
401
341
|
import { existsSync, readFileSync } from "fs";
|
|
402
342
|
import path from "path";
|
|
@@ -510,6 +450,142 @@ var resolveRuntimeMode = () => {
|
|
|
510
450
|
var isProductionRuntime = () => resolveRuntimeMode() === "production";
|
|
511
451
|
var isTestRuntime = () => resolveRuntimeMode() === "test";
|
|
512
452
|
|
|
453
|
+
// src/localSocketEnumerationGuard.ts
|
|
454
|
+
var ENUMERATING_MAP_MEMBERS = /* @__PURE__ */ new Set([
|
|
455
|
+
"values",
|
|
456
|
+
"keys",
|
|
457
|
+
"entries",
|
|
458
|
+
"forEach",
|
|
459
|
+
"size",
|
|
460
|
+
Symbol.iterator
|
|
461
|
+
]);
|
|
462
|
+
var PER_INSTANCE_ADAPTER_MEMBERS = /* @__PURE__ */ new Set(["rooms", "sids"]);
|
|
463
|
+
var LocalSocketEnumerationError = class extends Error {
|
|
464
|
+
constructor(surface) {
|
|
465
|
+
super(
|
|
466
|
+
`${surface} only sees the sockets connected to THIS instance. With the Redis adapter every other instance is invisible here, so a fan-out or membership check built on it silently misses them. Use \`getRoomSockets(room, { userId })\` from \`@luckystack/core\` or \`io.in(room).fetchSockets()\` for a cross-instance view, or \`getIoInstance({ raw: true })\` when you deliberately want the local one. (Thrown outside production only.)`
|
|
467
|
+
);
|
|
468
|
+
this.name = "LocalSocketEnumerationError";
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
var readBound = (target, prop) => {
|
|
472
|
+
const value = Reflect.get(target, prop);
|
|
473
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
474
|
+
};
|
|
475
|
+
var guardSocketsMap = (map) => new Proxy(map, {
|
|
476
|
+
get(target, prop) {
|
|
477
|
+
if (ENUMERATING_MAP_MEMBERS.has(prop)) {
|
|
478
|
+
throw new LocalSocketEnumerationError("Enumerating `io.sockets.sockets`");
|
|
479
|
+
}
|
|
480
|
+
return readBound(target, prop);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
var guardAdapter = (adapter) => new Proxy(adapter, {
|
|
484
|
+
get(target, prop) {
|
|
485
|
+
if (PER_INSTANCE_ADAPTER_MEMBERS.has(prop)) {
|
|
486
|
+
throw new LocalSocketEnumerationError(`\`io.sockets.adapter.${String(prop)}\``);
|
|
487
|
+
}
|
|
488
|
+
return readBound(target, prop);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
var guardNamespace = (namespace) => new Proxy(namespace, {
|
|
492
|
+
get(target, prop) {
|
|
493
|
+
if (prop === "adapter") return guardAdapter(target.adapter);
|
|
494
|
+
if (prop === "sockets") return guardSocketsMap(target.sockets);
|
|
495
|
+
return readBound(target, prop);
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
var guardLocalSocketEnumeration = (io) => new Proxy(io, {
|
|
499
|
+
get(target, prop) {
|
|
500
|
+
if (prop === "sockets") return guardNamespace(target.sockets);
|
|
501
|
+
return readBound(target, prop);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// src/socketTypes.ts
|
|
506
|
+
var _ioInstance = null;
|
|
507
|
+
var _guardedIoInstance = null;
|
|
508
|
+
var setIoInstance = (io) => {
|
|
509
|
+
_ioInstance = io;
|
|
510
|
+
_guardedIoInstance = null;
|
|
511
|
+
};
|
|
512
|
+
var getIoInstance = (options) => {
|
|
513
|
+
if (_ioInstance === null) return null;
|
|
514
|
+
if (options?.raw === true || isProductionRuntime()) return _ioInstance;
|
|
515
|
+
_guardedIoInstance ??= guardLocalSocketEnumeration(_ioInstance);
|
|
516
|
+
return _guardedIoInstance;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// src/roomSockets.ts
|
|
520
|
+
var getRoomSockets = async (room, options = {}) => {
|
|
521
|
+
const io = getIoInstance({ raw: true });
|
|
522
|
+
if (io === null) {
|
|
523
|
+
throw new Error("getRoomSockets: no Socket.io server is registered (setIoInstance was never called), so the sockets of a room cannot be resolved.");
|
|
524
|
+
}
|
|
525
|
+
const logicalRoom = room.trim();
|
|
526
|
+
if (logicalRoom === "") {
|
|
527
|
+
throw new Error("getRoomSockets: `room` must be a non-empty room code.");
|
|
528
|
+
}
|
|
529
|
+
if (logicalRoom === "all") return io.fetchSockets();
|
|
530
|
+
const physicalRoom = formatRoomName(logicalRoom, { purpose: "broadcast", userId: options.userId ?? null });
|
|
531
|
+
return io.in(physicalRoom).fetchSockets();
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// src/runtimeMapsRegistry.ts
|
|
535
|
+
var emptyApi = { apisObject: {}, functionsObject: {} };
|
|
536
|
+
var emptySync = { syncObject: {}, functionsObject: {} };
|
|
537
|
+
var registry3 = createRegistry({
|
|
538
|
+
getRuntimeApiMaps: () => Promise.resolve(emptyApi),
|
|
539
|
+
getRuntimeSyncMaps: () => Promise.resolve(emptySync)
|
|
540
|
+
});
|
|
541
|
+
var registerRuntimeMapsProvider = (provider2) => {
|
|
542
|
+
registry3.register(provider2);
|
|
543
|
+
};
|
|
544
|
+
var isRuntimeMapsProviderRegistered = () => registry3.isRegistered();
|
|
545
|
+
var getRuntimeApiMaps = async () => registry3.get().getRuntimeApiMaps();
|
|
546
|
+
var getRuntimeSyncMaps = async () => registry3.get().getRuntimeSyncMaps();
|
|
547
|
+
|
|
548
|
+
// src/emailRegistry.ts
|
|
549
|
+
var activeSender = null;
|
|
550
|
+
var registry4 = {};
|
|
551
|
+
var registerEmailSender = (sender) => {
|
|
552
|
+
activeSender = sender;
|
|
553
|
+
registry4 = { ...registry4, default: sender };
|
|
554
|
+
};
|
|
555
|
+
var registerEmailSenders = (senders) => {
|
|
556
|
+
registry4 = { ...senders };
|
|
557
|
+
if (senders.default) {
|
|
558
|
+
activeSender = senders.default;
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
var getEmailSender = () => activeSender ?? registry4.default ?? null;
|
|
562
|
+
var getEmailSenderByName = (name) => {
|
|
563
|
+
if (registry4[name]) return registry4[name];
|
|
564
|
+
if (name === "default" && activeSender) return activeSender;
|
|
565
|
+
return null;
|
|
566
|
+
};
|
|
567
|
+
var listEmailSenderNames = () => Object.keys(registry4);
|
|
568
|
+
var isEmailSenderRegistered = () => activeSender !== null || Object.keys(registry4).length > 0;
|
|
569
|
+
|
|
570
|
+
// src/localeReloader.ts
|
|
571
|
+
var registry5 = createRegistry(null);
|
|
572
|
+
var registerLocaleReloader = (reloader) => {
|
|
573
|
+
registry5.register(reloader);
|
|
574
|
+
return reloader;
|
|
575
|
+
};
|
|
576
|
+
var getLocaleReloader = () => registry5.get();
|
|
577
|
+
|
|
578
|
+
// src/errorTrackerIdentity.ts
|
|
579
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
580
|
+
var identityStore = new AsyncLocalStorage();
|
|
581
|
+
var runWithErrorTrackerIdentityScope = (fn) => identityStore.run({ user: null }, fn);
|
|
582
|
+
var runWithErrorTrackerIdentity = (user, fn) => identityStore.run({ user }, fn);
|
|
583
|
+
var setCurrentErrorTrackerIdentity = (user) => {
|
|
584
|
+
const box = identityStore.getStore();
|
|
585
|
+
if (box) box.user = user;
|
|
586
|
+
};
|
|
587
|
+
var getCurrentErrorTrackerIdentity = () => identityStore.getStore()?.user ?? null;
|
|
588
|
+
|
|
513
589
|
// src/db.ts
|
|
514
590
|
import { createRequire as createRequire2 } from "module";
|
|
515
591
|
|
|
@@ -865,17 +941,6 @@ var releaseLease = async (name, token) => {
|
|
|
865
941
|
|
|
866
942
|
// src/socketRedisAdapter.ts
|
|
867
943
|
import { createAdapter } from "@socket.io/redis-adapter";
|
|
868
|
-
var attachSocketRedisAdapter = (io, options = {}) => {
|
|
869
|
-
const pubClient = options.pubClient ?? redis.duplicate();
|
|
870
|
-
const subClient = options.subClient ?? redis.duplicate();
|
|
871
|
-
pubClient.on("error", (err) => {
|
|
872
|
-
getLogger().error("[socket-redis-adapter] pub client error", err);
|
|
873
|
-
});
|
|
874
|
-
subClient.on("error", (err) => {
|
|
875
|
-
getLogger().error("[socket-redis-adapter] sub client error", err);
|
|
876
|
-
});
|
|
877
|
-
io.adapter(createAdapter(pubClient, subClient, options.adapterOptions));
|
|
878
|
-
};
|
|
879
944
|
|
|
880
945
|
// src/bootUuid.ts
|
|
881
946
|
import { randomUUID } from "crypto";
|
|
@@ -936,6 +1001,23 @@ var startBootUuidHeartbeat = (envKey) => {
|
|
|
936
1001
|
};
|
|
937
1002
|
};
|
|
938
1003
|
|
|
1004
|
+
// src/socketRedisAdapter.ts
|
|
1005
|
+
var resolveSocketAdapterKey = () => `${getProjectName()}:${resolveEnvKey()}:socket.io`;
|
|
1006
|
+
var attachSocketRedisAdapter = (io, options = {}) => {
|
|
1007
|
+
const pubClient = options.pubClient ?? redis.duplicate();
|
|
1008
|
+
const subClient = options.subClient ?? redis.duplicate();
|
|
1009
|
+
pubClient.on("error", (err) => {
|
|
1010
|
+
getLogger().error("[socket-redis-adapter] pub client error", err);
|
|
1011
|
+
});
|
|
1012
|
+
subClient.on("error", (err) => {
|
|
1013
|
+
getLogger().error("[socket-redis-adapter] sub client error", err);
|
|
1014
|
+
});
|
|
1015
|
+
io.adapter(createAdapter(pubClient, subClient, {
|
|
1016
|
+
...options.adapterOptions,
|
|
1017
|
+
key: options.adapterOptions?.key ?? resolveSocketAdapterKey()
|
|
1018
|
+
}));
|
|
1019
|
+
};
|
|
1020
|
+
|
|
939
1021
|
// src/synchronizedEnvHashes.ts
|
|
940
1022
|
import { createHash, createHmac } from "crypto";
|
|
941
1023
|
var plainHash = (value) => createHash("sha256").update(value).digest("hex");
|
|
@@ -2257,9 +2339,11 @@ export {
|
|
|
2257
2339
|
GENERATED_API_SCHEMAS_PATH,
|
|
2258
2340
|
GENERATED_SOCKET_TYPES_PATH,
|
|
2259
2341
|
INVALID_ERROR_RESPONSE_CODE,
|
|
2342
|
+
LocalSocketEnumerationError,
|
|
2260
2343
|
PUBLIC_DIR,
|
|
2261
2344
|
REDACTED_PLACEHOLDER,
|
|
2262
2345
|
ROOT_DIR,
|
|
2346
|
+
ROUTED_DATA_QUERY_KEY,
|
|
2263
2347
|
SERVER_DIR,
|
|
2264
2348
|
SERVER_FUNCTIONS_DIR,
|
|
2265
2349
|
SHARED_DIR,
|
|
@@ -2286,6 +2370,7 @@ export {
|
|
|
2286
2370
|
buildGetJoinedRoomsResponseEventName,
|
|
2287
2371
|
buildJoinRoomResponseEventName,
|
|
2288
2372
|
buildLeaveRoomResponseEventName,
|
|
2373
|
+
buildRoutedGetUrl,
|
|
2289
2374
|
buildSyncProgressEventName,
|
|
2290
2375
|
buildSyncResponseEventName,
|
|
2291
2376
|
captureException,
|
|
@@ -2370,6 +2455,7 @@ export {
|
|
|
2370
2455
|
getRegisteredApiMethod,
|
|
2371
2456
|
getRegisteredSessionProvider,
|
|
2372
2457
|
getRoomNameFormatter,
|
|
2458
|
+
getRoomSockets,
|
|
2373
2459
|
getRuntimeApiMaps,
|
|
2374
2460
|
getRuntimeSyncMaps,
|
|
2375
2461
|
getServerDir,
|
|
@@ -2494,6 +2580,7 @@ export {
|
|
|
2494
2580
|
resolveHealthHashConfig,
|
|
2495
2581
|
resolveHealthHashConfigFromDescriptor,
|
|
2496
2582
|
resolveRuntimeMode,
|
|
2583
|
+
resolveSocketAdapterKey,
|
|
2497
2584
|
runWithErrorTrackerIdentity,
|
|
2498
2585
|
runWithErrorTrackerIdentityScope,
|
|
2499
2586
|
sanitizeErrorString,
|