@spfn/core 0.3.0-beta.27 → 0.3.0-beta.29
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/codegen/index.d.ts +65 -2
- package/dist/codegen/index.js +160 -15
- package/dist/codegen/index.js.map +1 -1
- package/dist/contract/index.d.ts +2 -2
- package/dist/db/index.d.ts +39 -11
- package/dist/db/index.js +34 -14
- package/dist/db/index.js.map +1 -1
- package/dist/{error-TsUIjp_x.d.ts → error-CIZlYJlL.d.ts} +1 -1
- package/dist/event/index.d.ts +3 -3
- package/dist/event/index.js +10 -0
- package/dist/event/index.js.map +1 -1
- package/dist/event/sse/client.d.ts +2 -2
- package/dist/event/sse/client.js +27 -17
- package/dist/event/sse/client.js.map +1 -1
- package/dist/event/sse/index.d.ts +3 -3
- package/dist/event/sse/index.js +115 -34
- package/dist/event/sse/index.js.map +1 -1
- package/dist/event/ws/client.d.ts +2 -2
- package/dist/event/ws/index.d.ts +3 -3
- package/dist/ops/index.d.ts +3 -3
- package/dist/ops/index.js +15 -5
- package/dist/ops/index.js.map +1 -1
- package/dist/route/index.d.ts +2 -2
- package/dist/route/index.js +15 -5
- package/dist/route/index.js.map +1 -1
- package/dist/{router-4YoIy5fN.d.ts → router-C57y-vz1.d.ts} +12 -0
- package/dist/{router-D5B0cpVN.d.ts → router-CX5uVjmU.d.ts} +29 -1
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +130 -39
- package/dist/server/index.js.map +1 -1
- package/dist/state/client.d.ts +73 -11
- package/dist/state/client.js +231 -31
- package/dist/state/client.js.map +1 -1
- package/dist/state/index.js +19 -4
- package/dist/state/index.js.map +1 -1
- package/dist/state/react.d.ts +18 -3
- package/dist/state/react.js +11 -5
- package/dist/state/react.js.map +1 -1
- package/dist/surface/index.d.ts +3 -3
- package/dist/surface/index.js +15 -5
- package/dist/surface/index.js.map +1 -1
- package/dist/{types-04gu3uMj.d.ts → types-CurMab2Q.d.ts} +19 -2
- package/dist/{types-CqTsDETV.d.ts → types-DncqXfWN.d.ts} +1 -1
- package/package.json +1 -1
- package/src/codegen/README.md +89 -2
- package/src/db/transaction/README.md +58 -35
- package/src/event/README.md +61 -14
- package/src/state/README.md +228 -35
package/dist/server/index.js
CHANGED
|
@@ -429,7 +429,15 @@ function loadEnvFiles() {
|
|
|
429
429
|
function createBoundedWriter(stream, maxQueue, onClose) {
|
|
430
430
|
const pending = [];
|
|
431
431
|
let flushing = false;
|
|
432
|
+
let reserved = 0;
|
|
432
433
|
let closed = false;
|
|
434
|
+
const overflows = () => {
|
|
435
|
+
if (pending.length + reserved <= maxQueue) {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
onClose("outbound queue overflow (slow consumer)");
|
|
439
|
+
return true;
|
|
440
|
+
};
|
|
433
441
|
const flush = async () => {
|
|
434
442
|
if (flushing || closed) {
|
|
435
443
|
return;
|
|
@@ -453,11 +461,19 @@ function createBoundedWriter(stream, maxQueue, onClose) {
|
|
|
453
461
|
return;
|
|
454
462
|
}
|
|
455
463
|
pending.push(frame);
|
|
456
|
-
if (
|
|
457
|
-
|
|
458
|
-
|
|
464
|
+
if (!overflows()) {
|
|
465
|
+
void flush();
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
reserve() {
|
|
469
|
+
if (closed) {
|
|
470
|
+
return false;
|
|
459
471
|
}
|
|
460
|
-
|
|
472
|
+
reserved++;
|
|
473
|
+
return !overflows();
|
|
474
|
+
},
|
|
475
|
+
clearReserved() {
|
|
476
|
+
reserved = 0;
|
|
461
477
|
},
|
|
462
478
|
close() {
|
|
463
479
|
closed = true;
|
|
@@ -709,7 +725,7 @@ async function authorizeConnection(c, router, authConfig, subject, requested) {
|
|
|
709
725
|
subject: subject || void 0,
|
|
710
726
|
clientIp: c.req.header("x-forwarded-for") || c.req.header("x-real-ip")
|
|
711
727
|
});
|
|
712
|
-
return { subject, requested, allowed, denied, request };
|
|
728
|
+
return { subject, requested, allowed, denied, request, principal: resolved.principal };
|
|
713
729
|
}
|
|
714
730
|
async function resolveConnectionPrincipal(subject, request, authConfig) {
|
|
715
731
|
if (!subject || !authConfig?.resolvePrincipal) {
|
|
@@ -765,6 +781,16 @@ async function runStream(c, stream, admission, config) {
|
|
|
765
781
|
sseLogger.warn("SSE connection closed", { events: [...connection.allowed], reason });
|
|
766
782
|
cleanup(reason);
|
|
767
783
|
});
|
|
784
|
+
const hold = createFrameHold(writer);
|
|
785
|
+
const subscriberContext = {
|
|
786
|
+
router: config.router,
|
|
787
|
+
authConfig: config.authConfig,
|
|
788
|
+
subject,
|
|
789
|
+
request,
|
|
790
|
+
writeFrame: createFrameWriter(writer),
|
|
791
|
+
send: hold.send,
|
|
792
|
+
isDead: () => connectionDead
|
|
793
|
+
};
|
|
768
794
|
const connection = {
|
|
769
795
|
subject,
|
|
770
796
|
requested: admission.requested,
|
|
@@ -772,45 +798,61 @@ async function runStream(c, stream, admission, config) {
|
|
|
772
798
|
denied: new Set(denied),
|
|
773
799
|
unsubscribes: /* @__PURE__ */ new Map(),
|
|
774
800
|
request,
|
|
775
|
-
subscribe: createEventSubscriber(
|
|
776
|
-
router: config.router,
|
|
777
|
-
authConfig: config.authConfig,
|
|
778
|
-
subject,
|
|
779
|
-
request,
|
|
780
|
-
writer,
|
|
781
|
-
isDead: () => connectionDead
|
|
782
|
-
}),
|
|
801
|
+
subscribe: createEventSubscriber(subscriberContext),
|
|
783
802
|
notify: (frame, eventName) => {
|
|
784
803
|
if (connectionDead) return;
|
|
785
|
-
writer.enqueue({ event: frame, data: JSON.stringify({ event: eventName }) });
|
|
804
|
+
hold.send(() => writer.enqueue({ event: frame, data: JSON.stringify({ event: eventName }) }));
|
|
786
805
|
}
|
|
787
806
|
};
|
|
788
807
|
if (admission.pending?.open(connection) === false) {
|
|
789
808
|
cleanup("aborted during admission");
|
|
790
809
|
return;
|
|
791
810
|
}
|
|
792
|
-
openSubscriptions(connection, writer
|
|
811
|
+
void openSubscriptions(connection, writer, hold, {
|
|
812
|
+
...subscriberContext,
|
|
813
|
+
principal: admission.principal
|
|
814
|
+
});
|
|
793
815
|
pingTimer = setInterval(() => {
|
|
794
816
|
if (connectionDead) return;
|
|
795
|
-
writer.enqueue({
|
|
817
|
+
hold.send(() => writer.enqueue({
|
|
796
818
|
event: "ping",
|
|
797
819
|
data: JSON.stringify({ timestamp: Date.now() })
|
|
798
|
-
});
|
|
820
|
+
}));
|
|
799
821
|
}, config.pingInterval);
|
|
800
822
|
await waitForDisconnect(c.req.raw.signal, () => connectionDead, (resolve2) => {
|
|
801
823
|
onClosed = resolve2;
|
|
802
824
|
});
|
|
803
825
|
cleanup();
|
|
804
826
|
}
|
|
805
|
-
function openSubscriptions(connection, writer) {
|
|
827
|
+
async function openSubscriptions(connection, writer, hold, ctx) {
|
|
828
|
+
const connected = { subscribedEvents: [...connection.allowed], deniedEvents: [...connection.denied] };
|
|
829
|
+
const producing = connected.subscribedEvents.filter((eventName) => ctx.router.events[eventName]._initial);
|
|
830
|
+
if (!ctx.subject || producing.length === 0) {
|
|
831
|
+
enqueueConnected(writer, { ...connected, initial: [] });
|
|
832
|
+
subscribeAllowed(connection);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
hold.hold();
|
|
836
|
+
subscribeAllowed(connection);
|
|
837
|
+
const served = await produceInitialFrames(producing, ctx.router, {
|
|
838
|
+
subject: ctx.subject,
|
|
839
|
+
principal: ctx.principal,
|
|
840
|
+
request: ctx.request
|
|
841
|
+
});
|
|
842
|
+
if (ctx.isDead()) {
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
enqueueConnected(writer, { ...connected, initial: served.map(({ eventName }) => eventName) });
|
|
846
|
+
served.forEach(({ eventName, frames }) => frames.forEach((data) => ctx.writeFrame(eventName, data)));
|
|
847
|
+
hold.release();
|
|
848
|
+
}
|
|
849
|
+
function enqueueConnected(writer, connected) {
|
|
806
850
|
writer.enqueue({
|
|
807
851
|
event: "connected",
|
|
808
|
-
data: JSON.stringify({
|
|
809
|
-
subscribedEvents: [...connection.allowed],
|
|
810
|
-
deniedEvents: [...connection.denied],
|
|
811
|
-
timestamp: Date.now()
|
|
812
|
-
})
|
|
852
|
+
data: JSON.stringify({ ...connected, timestamp: Date.now() })
|
|
813
853
|
});
|
|
854
|
+
}
|
|
855
|
+
function subscribeAllowed(connection) {
|
|
814
856
|
for (const eventName of connection.allowed) {
|
|
815
857
|
connection.unsubscribes.set(eventName, connection.subscribe(eventName));
|
|
816
858
|
}
|
|
@@ -820,6 +862,54 @@ function openSubscriptions(connection, writer) {
|
|
|
820
862
|
subscriptionCount: connection.unsubscribes.size
|
|
821
863
|
});
|
|
822
864
|
}
|
|
865
|
+
async function produceInitialFrames(events, router, ctx) {
|
|
866
|
+
const results = await Promise.all(events.map((eventName) => askInitial(eventName, router.events[eventName], ctx)));
|
|
867
|
+
return results.filter((frames) => frames !== null);
|
|
868
|
+
}
|
|
869
|
+
async function askInitial(eventName, eventDef, ctx) {
|
|
870
|
+
try {
|
|
871
|
+
const payloads = await eventDef._initial(ctx);
|
|
872
|
+
return { eventName, frames: payloads.map((payload) => serializeFrame(eventName, payload)) };
|
|
873
|
+
} catch {
|
|
874
|
+
sseLogger.warn("SSE initial frames could not be produced \u2014 none sent for this event", {
|
|
875
|
+
event: eventName,
|
|
876
|
+
subject: ctx.subject
|
|
877
|
+
});
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function createFrameHold(writer) {
|
|
882
|
+
let pending = null;
|
|
883
|
+
return {
|
|
884
|
+
send: (write) => {
|
|
885
|
+
if (!pending) {
|
|
886
|
+
write();
|
|
887
|
+
} else if (writer.reserve()) {
|
|
888
|
+
pending.push(write);
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
hold: () => {
|
|
892
|
+
pending = [];
|
|
893
|
+
},
|
|
894
|
+
release: () => {
|
|
895
|
+
const held = pending ?? [];
|
|
896
|
+
pending = null;
|
|
897
|
+
writer.clearReserved();
|
|
898
|
+
held.forEach((write) => write());
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
function createFrameWriter(writer) {
|
|
903
|
+
let messageId = 0;
|
|
904
|
+
return (eventName, data) => {
|
|
905
|
+
messageId++;
|
|
906
|
+
sseLogger.debug("SSE sending event", {
|
|
907
|
+
event: eventName,
|
|
908
|
+
messageId
|
|
909
|
+
});
|
|
910
|
+
writer.enqueue({ id: String(messageId), event: eventName, data });
|
|
911
|
+
};
|
|
912
|
+
}
|
|
823
913
|
async function waitForDisconnect(abortSignal, isDead, onServerClose) {
|
|
824
914
|
if (abortSignal.aborted || isDead()) {
|
|
825
915
|
return;
|
|
@@ -834,7 +924,6 @@ async function waitForDisconnect(abortSignal, isDead, onServerClose) {
|
|
|
834
924
|
});
|
|
835
925
|
}
|
|
836
926
|
function createEventSubscriber(ctx) {
|
|
837
|
-
let messageId = 0;
|
|
838
927
|
return (eventName) => {
|
|
839
928
|
const eventDef = ctx.router.events[eventName];
|
|
840
929
|
let active = true;
|
|
@@ -842,16 +931,8 @@ function createEventSubscriber(ctx) {
|
|
|
842
931
|
if (!active || ctx.isDead() || !passesFrameFilters(eventName, eventDef, payload, ctx)) {
|
|
843
932
|
return;
|
|
844
933
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
event: eventName,
|
|
848
|
-
messageId
|
|
849
|
-
});
|
|
850
|
-
ctx.writer.enqueue({
|
|
851
|
-
id: String(messageId),
|
|
852
|
-
event: eventName,
|
|
853
|
-
data: serializeFrame(eventName, payload)
|
|
854
|
-
});
|
|
934
|
+
const data = serializeFrame(eventName, payload);
|
|
935
|
+
ctx.send(() => ctx.writeFrame(eventName, data));
|
|
855
936
|
});
|
|
856
937
|
return () => {
|
|
857
938
|
active = false;
|
|
@@ -2088,7 +2169,7 @@ var route = {
|
|
|
2088
2169
|
|
|
2089
2170
|
// src/route/router.ts
|
|
2090
2171
|
function createRouterInstance(state2) {
|
|
2091
|
-
|
|
2172
|
+
const router = {
|
|
2092
2173
|
routes: state2.routes,
|
|
2093
2174
|
_routes: state2.routes,
|
|
2094
2175
|
_packageRouters: state2.packageRouters,
|
|
@@ -2103,19 +2184,29 @@ function createRouterInstance(state2) {
|
|
|
2103
2184
|
}
|
|
2104
2185
|
}
|
|
2105
2186
|
assertDistinctSurfaces(newPackageRouters);
|
|
2106
|
-
return createRouterInstance({ ...state2, packageRouters: newPackageRouters });
|
|
2187
|
+
return carryMarkers(router, createRouterInstance({ ...state2, packageRouters: newPackageRouters }));
|
|
2107
2188
|
},
|
|
2108
2189
|
use(middlewares) {
|
|
2109
|
-
return createRouterInstance({
|
|
2190
|
+
return carryMarkers(router, createRouterInstance({
|
|
2110
2191
|
...state2,
|
|
2111
2192
|
globalMiddlewares: [...state2.globalMiddlewares, ...middlewares]
|
|
2112
|
-
});
|
|
2193
|
+
}));
|
|
2113
2194
|
},
|
|
2114
2195
|
contractVersion(version) {
|
|
2115
2196
|
assertContractVersion(version);
|
|
2116
|
-
return createRouterInstance({ ...state2, contractVersion: version });
|
|
2197
|
+
return carryMarkers(router, createRouterInstance({ ...state2, contractVersion: version }));
|
|
2117
2198
|
}
|
|
2118
2199
|
};
|
|
2200
|
+
return router;
|
|
2201
|
+
}
|
|
2202
|
+
var MARKERS = ["_surfaceNamespace", "_stateManifest"];
|
|
2203
|
+
function carryMarkers(from, to) {
|
|
2204
|
+
for (const marker of MARKERS) {
|
|
2205
|
+
if (from[marker] !== void 0) {
|
|
2206
|
+
Object.defineProperty(to, marker, { value: from[marker], enumerable: false });
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
return to;
|
|
2119
2210
|
}
|
|
2120
2211
|
function assertContractVersion(version) {
|
|
2121
2212
|
if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)*$/.test(version)) {
|