@pipeline-builder/api-server 3.4.130 → 3.4.132
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/lib/api/app-factory.d.ts +9 -0
- package/lib/api/app-factory.js +26 -5
- package/lib/api/idempotency-middleware.d.ts +32 -2
- package/lib/api/idempotency-middleware.js +73 -3
- package/lib/api/request-types.js +8 -3
- package/lib/api/route-wrapper.js +6 -2
- package/lib/api/tenant-context.js +4 -1
- package/lib/http/index.d.ts +1 -0
- package/lib/http/index.js +2 -1
- package/lib/http/sse-connection-manager.d.ts +55 -13
- package/lib/http/sse-connection-manager.js +70 -39
- package/lib/http/sse-ticket-store.d.ts +69 -0
- package/lib/http/sse-ticket-store.js +160 -0
- package/package.json +4 -4
package/lib/api/route-wrapper.js
CHANGED
|
@@ -40,7 +40,11 @@ export function withRoute(handler, options = {}) {
|
|
|
40
40
|
const { requireOrgId = true } = options;
|
|
41
41
|
return async (req, res) => {
|
|
42
42
|
const ctx = getContext(req);
|
|
43
|
-
|
|
43
|
+
// `identity.orgId` is already normalized (trimmed + lowercased) once at
|
|
44
|
+
// resolution in api-core's `getIdentity`, so the previously-duplicated
|
|
45
|
+
// `.toLowerCase()` here is redundant — kept normalization at the single
|
|
46
|
+
// source of truth instead. This value now matches the RLS GUC exactly.
|
|
47
|
+
const orgId = ctx.identity.orgId || '';
|
|
44
48
|
const userId = ctx.identity.userId || '';
|
|
45
49
|
if (requireOrgId && !orgId) {
|
|
46
50
|
return sendBadRequest(res, 'Organization ID is required');
|
|
@@ -77,4 +81,4 @@ export function withRoute(handler, options = {}) {
|
|
|
77
81
|
}
|
|
78
82
|
};
|
|
79
83
|
}
|
|
80
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
84
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtd3JhcHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvcm91dGUtd3JhcHBlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFDTCxRQUFRLEVBQ1IsWUFBWSxFQUNaLGNBQWMsRUFDZCxTQUFTLEVBQ1QsY0FBYyxFQUNkLGlCQUFpQixFQUNqQixZQUFZLEdBQ2IsTUFBTSw0QkFBNEIsQ0FBQztBQUVwQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFHOUMsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLGVBQWUsQ0FBQyxDQUFDO0FBRTdDOzs7OztHQUtHO0FBQ0gsU0FBUyxlQUFlLENBQUMsS0FBYztJQUNyQyxJQUFJLEtBQUssWUFBWSxLQUFLLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQztRQUN4RSxPQUFPLEdBQUcsS0FBSyxDQUFDLElBQUksSUFBSSxtQkFBbUIseUJBQXlCLENBQUM7SUFDdkUsQ0FBQztJQUNELE9BQU8sWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQzdCLENBQUM7QUE4QkQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBQ0gsTUFBTSxVQUFVLFNBQVMsQ0FDdkIsT0FBNEMsRUFDNUMsT0FBTyxHQUFxQixFQUFFO0lBRTlCLE1BQU0sRUFBRSxZQUFZLEdBQUcsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDO0lBRXhDLE9BQU8sS0FBSyxFQUFFLEdBQVksRUFBRSxHQUFhLEVBQUUsRUFBRTtRQUMzQyxNQUFNLEdBQUcsR0FBRyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDNUIsd0VBQXdFO1FBQ3hFLHVFQUF1RTtRQUN2RSx3RUFBd0U7UUFDeEUsdUVBQXVFO1FBQ3ZFLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxRQUFRLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUN2QyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsUUFBUSxDQUFDLE1BQU0sSUFBSSxFQUFFLENBQUM7UUFFekMsSUFBSSxZQUFZLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUMzQixPQUFPLGNBQWMsQ0FBQyxHQUFHLEVBQUUsNkJBQTZCLENBQUMsQ0FBQztRQUM1RCxDQUFDO1FBRUQsSUFBSSxDQUFDO1lBQ0gsTUFBTSxPQUFPLENBQUMsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUNsRCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLHNEQUFzRDtZQUN0RCxJQUFJLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDcEIsTUFBTSxDQUFDLEtBQUssQ0FBQyx5Q0FBeUMsRUFBRTtvQkFDdEQsU0FBUyxFQUFFLEdBQUcsQ0FBQyxTQUFTO29CQUN4QixLQUFLLEVBQUUsZUFBZSxDQUFDLEtBQUssQ0FBQztpQkFDOUIsQ0FBQyxDQUFDO2dCQUNILE9BQU87WUFDVCxDQUFDO1lBRUQsSUFBSSxLQUFLLFlBQVksUUFBUSxFQUFFLENBQUM7Z0JBQzlCLE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxVQUFVLElBQUksR0FBRyxJQUFJLEtBQUssQ0FBQyxVQUFVLEdBQUcsR0FBRztvQkFDOUQsQ0FBQyxDQUFDLEtBQUssQ0FBQyxVQUFVO29CQUNsQixDQUFDLENBQUMsR0FBRyxDQUFDO2dCQUNSLE9BQU8sU0FBUyxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDM0QsQ0FBQztZQUVELDZFQUE2RTtZQUM3RSw4RUFBOEU7WUFDOUUsK0VBQStFO1lBQy9FLDhFQUE4RTtZQUM5RSxNQUFNLEVBQUUsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDakMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUU7Z0JBQ2pDLFNBQVMsRUFBRSxHQUFHLENBQUMsU0FBUztnQkFDeEIsS0FBSyxFQUFFLGVBQWUsQ0FBQyxLQUFLLENBQUM7Z0JBQzdCLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2FBQzFDLENBQUMsQ0FBQztZQUNILE9BQU8saUJBQWlCLENBQUMsR0FBRyxFQUFFLHVCQUF1QixFQUFFLEVBQUUsU0FBUyxFQUFFLEdBQUcsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZGLENBQUM7SUFDSCxDQUFDLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7XG4gIEFwcEVycm9yLFxuICBlcnJvck1lc3NhZ2UsXG4gIGV4dHJhY3REYkVycm9yLFxuICBzZW5kRXJyb3IsXG4gIHNlbmRCYWRSZXF1ZXN0LFxuICBzZW5kSW50ZXJuYWxFcnJvcixcbiAgY3JlYXRlTG9nZ2VyLFxufSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlLCBSZXF1ZXN0SGFuZGxlciB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgZ2V0Q29udGV4dCB9IGZyb20gJy4vZ2V0LWNvbnRleHQuanMnO1xuaW1wb3J0IHR5cGUgeyBSZXF1ZXN0Q29udGV4dCB9IGZyb20gJy4vcmVxdWVzdC10eXBlcy5qcyc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcigncm91dGUtd3JhcHBlcicpO1xuXG4vKipcbiAqIEEgbG9nLXNhZmUgcmVuZGVyaW5nIG9mIGFuIHVua25vd24gZXJyb3IuIGRyaXp6bGUtb3JtJ3MgYERyaXp6bGVRdWVyeUVycm9yYFxuICogZW1iZWRzIHRoZSBmYWlsZWQgU1FMIHN0YXRlbWVudCBBTkQgdGhlIGJvdW5kIHBhcmFtZXRlciB2YWx1ZXMgKHJlYWwgZGF0YSkgaW5cbiAqIGl0cyBgLm1lc3NhZ2VgIChgRmFpbGVkIHF1ZXJ5OiA8c3FsPlxcbnBhcmFtczogPHZhbHVlcz5gKS4gTmV2ZXIgcHV0IHRoYXQgaW4gYVxuICogcmVzcG9uc2Ugb3IgYSBsb2cgbGluZSDigJQgY29sbGFwc2UgaXQgdG8gdGhlIGVycm9yIG5hbWUuIE90aGVyIGVycm9ycyBhcmUgc2FmZS5cbiAqL1xuZnVuY3Rpb24gc2FmZUVycm9yRm9yTG9nKGVycm9yOiB1bmtub3duKTogc3RyaW5nIHtcbiAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IgJiYgZXJyb3IubWVzc2FnZS5zdGFydHNXaXRoKCdGYWlsZWQgcXVlcnk6JykpIHtcbiAgICByZXR1cm4gYCR7ZXJyb3IubmFtZSB8fCAnRHJpenpsZVF1ZXJ5RXJyb3InfTogZGF0YWJhc2UgcXVlcnkgZmFpbGVkYDtcbiAgfVxuICByZXR1cm4gZXJyb3JNZXNzYWdlKGVycm9yKTtcbn1cblxuLyoqXG4gKiBDb250ZXh0IG9iamVjdCBwYXNzZWQgdG8gZXZlcnkgcm91dGUgaGFuZGxlciB3cmFwcGVkIHdpdGggYHdpdGhSb3V0ZSgpYC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSb3V0ZUNvbnRleHQge1xuICAvKiogT3JpZ2luYWwgRXhwcmVzcyByZXF1ZXN0ICovXG4gIHJlcTogUmVxdWVzdDtcbiAgLyoqIE9yaWdpbmFsIEV4cHJlc3MgcmVzcG9uc2UgKi9cbiAgcmVzOiBSZXNwb25zZTtcbiAgLyoqIFJlcXVlc3QgY29udGV4dCB3aXRoIGlkZW50aXR5IGFuZCBsb2dnaW5nICovXG4gIGN0eDogUmVxdWVzdENvbnRleHQ7XG4gIC8qKiBMb3dlcmNhc2VkIG9yZ2FuaXphdGlvbiBJRCAoZ3VhcmFudGVlZCBub24tZW1wdHkgd2hlbiByZXF1aXJlT3JnSWQgaXMgdHJ1ZSkgKi9cbiAgb3JnSWQ6IHN0cmluZztcbiAgLyoqIFVzZXIgSUQgZnJvbSBKV1QgKGRlZmF1bHRzIHRvIGVtcHR5IHN0cmluZykgKi9cbiAgdXNlcklkOiBzdHJpbmc7XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgYHdpdGhSb3V0ZSgpYC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBXaXRoUm91dGVPcHRpb25zIHtcbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gcmVxdWlyZSBvcmdJZCBvbiB0aGUgcmVxdWVzdC5cbiAgICogV2hlbiB0cnVlIChkZWZhdWx0KSwgcmV0dXJucyA0MDAgaWYgb3JnSWQgaXMgbWlzc2luZy5cbiAgICogU2V0IHRvIGZhbHNlIGZvciByb3V0ZXMgdGhhdCBkb24ndCBuZWVkIG9yZyBjb250ZXh0LlxuICAgKi9cbiAgcmVxdWlyZU9yZ0lkPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBXcmFwIGFuIGFzeW5jIHJvdXRlIGhhbmRsZXIgd2l0aCBzdGFuZGFyZCBib2lsZXJwbGF0ZS5cbiAqXG4gKiBFeHRyYWN0cyBjb250ZXh0LCBvcmdJZCwgdXNlcklkIGZyb20gdGhlIHJlcXVlc3QsIHZhbGlkYXRlcyBvcmdJZCxcbiAqIGFuZCBjYXRjaGVzIGVycm9ycy4gVHlwZWQgYEFwcEVycm9yYCBzdWJjbGFzc2VzIGFyZSBhdXRvbWF0aWNhbGx5XG4gKiBtYXBwZWQgdG8gdGhlIGNvcnJlY3QgSFRUUCByZXNwb25zZS5cbiAqXG4gKiBAcGFyYW0gaGFuZGxlciAtIEFzeW5jIGZ1bmN0aW9uIHJlY2VpdmluZyBhIFJvdXRlQ29udGV4dFxuICogQHBhcmFtIG9wdGlvbnMgLSBDb25maWd1cmF0aW9uIG9wdGlvbnNcbiAqIEByZXR1cm5zIEV4cHJlc3MgUmVxdWVzdEhhbmRsZXJcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogcm91dGVyLmdldCgnLzppZCcsIHdpdGhSb3V0ZShhc3luYyAoeyByZXEsIHJlcywgY3R4LCBvcmdJZCB9KSA9PiB7XG4gKiAgIGNvbnN0IGlkID0gZ2V0UGFyYW0ocmVxLnBhcmFtcywgJ2lkJyk7XG4gKiAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHBpcGVsaW5lU2VydmljZS5maW5kQnlJZChpZCwgb3JnSWQpO1xuICogICBpZiAoIXJlc3VsdCkgdGhyb3cgbmV3IE5vdEZvdW5kRXJyb3IoJ1BpcGVsaW5lIG5vdCBmb3VuZCcpO1xuICogICByZXR1cm4gc2VuZFN1Y2Nlc3MocmVzLCAyMDAsIHsgcGlwZWxpbmU6IHJlc3VsdCB9KTtcbiAqIH0pKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aFJvdXRlKFxuICBoYW5kbGVyOiAocmM6IFJvdXRlQ29udGV4dCkgPT4gUHJvbWlzZTx2b2lkPixcbiAgb3B0aW9uczogV2l0aFJvdXRlT3B0aW9ucyA9IHt9LFxuKTogUmVxdWVzdEhhbmRsZXIge1xuICBjb25zdCB7IHJlcXVpcmVPcmdJZCA9IHRydWUgfSA9IG9wdGlvbnM7XG5cbiAgcmV0dXJuIGFzeW5jIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UpID0+IHtcbiAgICBjb25zdCBjdHggPSBnZXRDb250ZXh0KHJlcSk7XG4gICAgLy8gYGlkZW50aXR5Lm9yZ0lkYCBpcyBhbHJlYWR5IG5vcm1hbGl6ZWQgKHRyaW1tZWQgKyBsb3dlcmNhc2VkKSBvbmNlIGF0XG4gICAgLy8gcmVzb2x1dGlvbiBpbiBhcGktY29yZSdzIGBnZXRJZGVudGl0eWAsIHNvIHRoZSBwcmV2aW91c2x5LWR1cGxpY2F0ZWRcbiAgICAvLyBgLnRvTG93ZXJDYXNlKClgIGhlcmUgaXMgcmVkdW5kYW50IOKAlCBrZXB0IG5vcm1hbGl6YXRpb24gYXQgdGhlIHNpbmdsZVxuICAgIC8vIHNvdXJjZSBvZiB0cnV0aCBpbnN0ZWFkLiBUaGlzIHZhbHVlIG5vdyBtYXRjaGVzIHRoZSBSTFMgR1VDIGV4YWN0bHkuXG4gICAgY29uc3Qgb3JnSWQgPSBjdHguaWRlbnRpdHkub3JnSWQgfHwgJyc7XG4gICAgY29uc3QgdXNlcklkID0gY3R4LmlkZW50aXR5LnVzZXJJZCB8fCAnJztcblxuICAgIGlmIChyZXF1aXJlT3JnSWQgJiYgIW9yZ0lkKSB7XG4gICAgICByZXR1cm4gc2VuZEJhZFJlcXVlc3QocmVzLCAnT3JnYW5pemF0aW9uIElEIGlzIHJlcXVpcmVkJyk7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGhhbmRsZXIoeyByZXEsIHJlcywgY3R4LCBvcmdJZCwgdXNlcklkIH0pO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBEb24ndCBzZW5kIGFub3RoZXIgcmVzcG9uc2UgaWYgb25lIHdhcyBhbHJlYWR5IHNlbnRcbiAgICAgIGlmIChyZXMuaGVhZGVyc1NlbnQpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdSb3V0ZSBoYW5kbGVyIGVycm9yIGFmdGVyIHJlc3BvbnNlIHNlbnQnLCB7XG4gICAgICAgICAgcmVxdWVzdElkOiBjdHgucmVxdWVzdElkLFxuICAgICAgICAgIGVycm9yOiBzYWZlRXJyb3JGb3JMb2coZXJyb3IpLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBBcHBFcnJvcikge1xuICAgICAgICBjb25zdCBzdGF0dXMgPSBlcnJvci5zdGF0dXNDb2RlID49IDQwMCAmJiBlcnJvci5zdGF0dXNDb2RlIDwgNjAwXG4gICAgICAgICAgPyBlcnJvci5zdGF0dXNDb2RlXG4gICAgICAgICAgOiA1MDA7XG4gICAgICAgIHJldHVybiBzZW5kRXJyb3IocmVzLCBzdGF0dXMsIGVycm9yLm1lc3NhZ2UsIGVycm9yLmNvZGUpO1xuICAgICAgfVxuXG4gICAgICAvLyBVbmhhbmRsZWQgZXJyb3Ig4oaSIDUwMC4gTkVWRVIgZWNobyB0aGUgcmF3IG1lc3NhZ2UgdG8gdGhlIGNsaWVudDogREIgZHJpdmVyXG4gICAgICAvLyBlcnJvcnMgY2FycnkgdGhlIFNRTCArIGJvdW5kIHBhcmFtcywgYW5kIENydWRTZXJ2aWNlIGRlbGliZXJhdGVseSBsZXRzIHRoZW1cbiAgICAgIC8vIHByb3BhZ2F0ZSBoZXJlLiBTZW5kIGEgZ2VuZXJpYyBtZXNzYWdlICsgcmVxdWVzdElkICh0byBjb3JyZWxhdGUgd2l0aCBsb2dzKSxcbiAgICAgIC8vIGFuZCBsb2cgb25seSBzYW5pdGl6ZWQgREIgbWV0YWRhdGEgKGNvZGUvY29uc3RyYWludC90YWJsZSDigJQgbm8gU1FML3BhcmFtcykuXG4gICAgICBjb25zdCBkYiA9IGV4dHJhY3REYkVycm9yKGVycm9yKTtcbiAgICAgIGN0eC5sb2coJ0VSUk9SJywgJ1JlcXVlc3QgZmFpbGVkJywge1xuICAgICAgICByZXF1ZXN0SWQ6IGN0eC5yZXF1ZXN0SWQsXG4gICAgICAgIGVycm9yOiBzYWZlRXJyb3JGb3JMb2coZXJyb3IpLFxuICAgICAgICAuLi4oT2JqZWN0LmtleXMoZGIpLmxlbmd0aCA/IHsgZGIgfSA6IHt9KSxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHNlbmRJbnRlcm5hbEVycm9yKHJlcywgJ0ludGVybmFsIHNlcnZlciBlcnJvcicsIHsgcmVxdWVzdElkOiBjdHgucmVxdWVzdElkIH0pO1xuICAgIH1cbiAgfTtcbn1cbiJdfQ==
|
|
@@ -7,6 +7,9 @@ import { getContext } from './get-context.js';
|
|
|
7
7
|
* the active-org parent (org → team hierarchy) so downstream side-effects can
|
|
8
8
|
* reach it without a request. */
|
|
9
9
|
const identityScope = (req) => ({
|
|
10
|
+
// `identity.orgId` is already normalized (trimmed + lowercased) once at
|
|
11
|
+
// resolution in api-core's `getIdentity`, so the GUC set here matches the
|
|
12
|
+
// app-layer WHERE clauses exactly — no ad-hoc re-normalization needed.
|
|
10
13
|
orgId: getContext(req).identity.orgId,
|
|
11
14
|
isSuperAdmin: isSystemAdmin(req),
|
|
12
15
|
parentOrgId: req.user?.parentOrganizationId,
|
|
@@ -40,4 +43,4 @@ export function withTenantContext(resolve = identityScope) {
|
|
|
40
43
|
runWithTenantContext(scope, () => next());
|
|
41
44
|
};
|
|
42
45
|
}
|
|
43
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
46
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW50LWNvbnRleHQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpL3RlbmFudC1jb250ZXh0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQzNELE9BQU8sRUFBRSxvQkFBb0IsRUFBc0IsTUFBTSxpQ0FBaUMsQ0FBQztBQUUzRixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFZOUM7O2tDQUVrQztBQUNsQyxNQUFNLGFBQWEsR0FBd0IsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDbkQsd0VBQXdFO0lBQ3hFLDBFQUEwRTtJQUMxRSx1RUFBdUU7SUFDdkUsS0FBSyxFQUFFLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSztJQUNyQyxZQUFZLEVBQUUsYUFBYSxDQUFDLEdBQUcsQ0FBQztJQUNoQyxXQUFXLEVBQUUsR0FBRyxDQUFDLElBQUksRUFBRSxvQkFBb0I7Q0FDNUMsQ0FBQyxDQUFDO0FBRUg7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUFDLE9BQU8sR0FBd0IsYUFBYTtJQUM1RSxPQUFPLENBQUMsR0FBWSxFQUFFLElBQWMsRUFBRSxJQUFrQixFQUFRLEVBQUU7UUFDaEUsSUFBSSxLQUFvQixDQUFDO1FBQ3pCLElBQUksQ0FBQztZQUNILEtBQUssR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDdkIsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDVixPQUFPO1FBQ1QsQ0FBQztRQUNELG9CQUFvQixDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzVDLENBQUMsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgaXNTeXN0ZW1BZG1pbiB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB7IHJ1bldpdGhUZW5hbnRDb250ZXh0LCB0eXBlIFRlbmFudENvbnRleHQgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9waXBlbGluZS1kYXRhJztcbmltcG9ydCB0eXBlIHsgUmVxdWVzdCwgUmVzcG9uc2UsIE5leHRGdW5jdGlvbiB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgZ2V0Q29udGV4dCB9IGZyb20gJy4vZ2V0LWNvbnRleHQuanMnO1xuXG4vKipcbiAqIFJlc29sdmVzIHRoZSBSTFMgdGVuYW50IHNjb3BlIGZvciBhIHJlcXVlc3QuIFRoZSBkZWZhdWx0IHJlYWRzIHRoZSBDQUxMRVInc1xuICogYXV0aGVudGljYXRlZCBpZGVudGl0eSBvcmcgKGBjdHguaWRlbnRpdHkub3JnSWRgLCB2YWxpZGF0ZWQgYnkgcmVxdWlyZU9yZ0lkKSDigJQgTk9UXG4gKiBgZ2V0T3JnSWQocmVxKWAsIHdoaWNoIHByZWZlcnMgcm91dGUgcGFyYW1zICh0aGUgcXVlcmllZCBzY29wZSksIHRoZSB3cm9uZyB0ZW5hbnQgZm9yXG4gKiBSTFMuIFNlcnZpY2VzIHdpdGggYSBkaWZmZXJlbnQgcmVxdWVzdCBib3VuZGFyeSBzdXBwbHkgdGhlaXIgb3duIHJlc29sdmVyIChlLmcuIHBsYXRmb3JtXG4gKiBzZXRzIHRoZSBzY29wZSBwcmUtYXV0aCBmcm9tIGFuIHVudmVyaWZpZWQgSldUIHBlZWsgc28gdW5hdXRoZW50aWNhdGVkIGVuZHBvaW50cyBzdGlsbFxuICogZ2V0IGEgc2FuZSBkZWZhdWx0KS5cbiAqL1xuZXhwb3J0IHR5cGUgVGVuYW50U2NvcGVSZXNvbHZlciA9IChyZXE6IFJlcXVlc3QpID0+IFRlbmFudENvbnRleHQ7XG5cbi8qKiBEZWZhdWx0IHJlc29sdmVyOiB0aGUgYXV0aGVudGljYXRlZCBpZGVudGl0eSdzIG9yZyArIHN1cGVyLWFkbWluIGZsYWcsIHBsdXNcbiAqICB0aGUgYWN0aXZlLW9yZyBwYXJlbnQgKG9yZyDihpIgdGVhbSBoaWVyYXJjaHkpIHNvIGRvd25zdHJlYW0gc2lkZS1lZmZlY3RzIGNhblxuICogIHJlYWNoIGl0IHdpdGhvdXQgYSByZXF1ZXN0LiAqL1xuY29uc3QgaWRlbnRpdHlTY29wZTogVGVuYW50U2NvcGVSZXNvbHZlciA9IChyZXEpID0+ICh7XG4gIC8vIGBpZGVudGl0eS5vcmdJZGAgaXMgYWxyZWFkeSBub3JtYWxpemVkICh0cmltbWVkICsgbG93ZXJjYXNlZCkgb25jZSBhdFxuICAvLyByZXNvbHV0aW9uIGluIGFwaS1jb3JlJ3MgYGdldElkZW50aXR5YCwgc28gdGhlIEdVQyBzZXQgaGVyZSBtYXRjaGVzIHRoZVxuICAvLyBhcHAtbGF5ZXIgV0hFUkUgY2xhdXNlcyBleGFjdGx5IOKAlCBubyBhZC1ob2MgcmUtbm9ybWFsaXphdGlvbiBuZWVkZWQuXG4gIG9yZ0lkOiBnZXRDb250ZXh0KHJlcSkuaWRlbnRpdHkub3JnSWQsXG4gIGlzU3VwZXJBZG1pbjogaXNTeXN0ZW1BZG1pbihyZXEpLFxuICBwYXJlbnRPcmdJZDogcmVxLnVzZXI/LnBhcmVudE9yZ2FuaXphdGlvbklkLFxufSk7XG5cbi8qKlxuICogRXN0YWJsaXNoIHRoZSByZXF1ZXN0J3Mgcm93LWxldmVsLXNlY3VyaXR5IHRlbmFudCBzY29wZS5cbiAqXG4gKiBSdW5zIHRoZSByZXN0IG9mIHRoZSByZXF1ZXN0IGluc2lkZSBgcnVuV2l0aFRlbmFudENvbnRleHQoeyBvcmdJZCwgaXNTdXBlckFkbWluIH0pYCAoYW5cbiAqIEFzeW5jTG9jYWxTdG9yYWdlIHNjb3BlKSBzbyBldmVyeSBgd2l0aFRlbmFudFR4YCBxdWVyeSBkb3duc3RyZWFtIGNhbiBgU0VUIExPQ0FMYCB0aGVcbiAqIFBvc3RncmVzIEdVQ3MgdGhhdCBSTFMgcG9saWNpZXMgcmVhZC4gV2l0aG91dCB0aGlzLCByZWFkcyBhZ2FpbnN0IEZPUkNFLVJMUyB0YWJsZXMgKGUuZy5cbiAqIGBwbHVnaW5zYCwgYHBpcGVsaW5lc2ApIHJ1biB3aXRoIGVtcHR5IEdVQ3MgYW5kIHNpbGVudGx5IHJldHVybiBubyByb3dzIOKAlCB0aGUgXCJzZXQgb25jZVxuICogYXQgdGhlIHJlcXVlc3QgYm91bmRhcnlcIiBib29rZW5kIHRoZSBSTFMgZW5mb3JjZW1lbnQgcGxhbiBjYWxscyBmb3IuXG4gKlxuICogRGVmYXVsdCBwbGFjZW1lbnQgaXMgQUZURVIgYHJlcXVpcmVBdXRoYCArIGByZXF1aXJlT3JnSWRgICh0aGUgcm91dGUgZmFjdG9yaWVzIGRvIHRoaXMpO1xuICogcGFzcyBhIGN1c3RvbSBgcmVzb2x2ZWAgZm9yIGEgZGlmZmVyZW50IGJvdW5kYXJ5LiBBIHRocm93biByZXNvbHZlciBtZWFucyB0ZW5hbnQgaWRlbnRpdHlcbiAqIGNvdWxkIE5PVCBiZSBlc3RhYmxpc2hlZCAoYSB3aXJpbmcgYnVnIOKAlCB0aGlzIHJ1bnMgYWZ0ZXIgYXV0aCkg4oCUIHdlIGhhcmQtRkFJTCB0aGUgcmVxdWVzdFxuICogKDUwMCB2aWEgbmV4dChlcnIpKSByYXRoZXIgdGhhbiBwcm9jZWVkLiBQcm9jZWVkaW5nIHdvdWxkIHJ1biBxdWVyaWVzIHdpdGggZW1wdHkgUkxTIEdVQ3M7XG4gKiB1bmRlciBGT1JDRSBSTFMgdGhhdCBoaWRlcyByb3dzLCBidXQgd2hpbGUgUkxTIGlzIGluIG93bmVyLWJ5cGFzcyBtb2RlIHRoZSBhcHAtbGF5ZXIgV0hFUkVcbiAqIGNsYXVzZSBpcyB0aGUgb25seSB0ZW5hbnQgZ2F0ZSwgc28gYSBzY29wZWxlc3MgcmVxdWVzdCBtdXN0IG5vdCBzaWxlbnRseSBjb250aW51ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdpdGhUZW5hbnRDb250ZXh0KHJlc29sdmU6IFRlbmFudFNjb3BlUmVzb2x2ZXIgPSBpZGVudGl0eVNjb3BlKSB7XG4gIHJldHVybiAocmVxOiBSZXF1ZXN0LCBfcmVzOiBSZXNwb25zZSwgbmV4dDogTmV4dEZ1bmN0aW9uKTogdm9pZCA9PiB7XG4gICAgbGV0IHNjb3BlOiBUZW5hbnRDb250ZXh0O1xuICAgIHRyeSB7XG4gICAgICBzY29wZSA9IHJlc29sdmUocmVxKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIG5leHQoZXJyKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgcnVuV2l0aFRlbmFudENvbnRleHQoc2NvcGUsICgpID0+IG5leHQoKSk7XG4gIH07XG59XG4iXX0=
|
package/lib/http/index.d.ts
CHANGED
package/lib/http/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Copyright 2026 Pipeline Builder Contributors
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
export * from './sse-connection-manager.js';
|
|
4
|
-
|
|
4
|
+
export * from './sse-ticket-store.js';
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaHR0cC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLGNBQWMsNkJBQTZCLENBQUM7QUFDNUMsY0FBYyx1QkFBdUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5leHBvcnQgKiBmcm9tICcuL3NzZS1jb25uZWN0aW9uLW1hbmFnZXIuanMnO1xuZXhwb3J0ICogZnJvbSAnLi9zc2UtdGlja2V0LXN0b3JlLmpzJztcbiJdfQ==
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Response } from 'express';
|
|
2
|
+
import { type SSETicketStore } from './sse-ticket-store.js';
|
|
2
3
|
/**
|
|
3
4
|
* Event types for SSE logging
|
|
4
5
|
*/
|
|
@@ -68,6 +69,19 @@ export interface SSEManagerOptions {
|
|
|
68
69
|
maxTicketsPerOrg?: number;
|
|
69
70
|
/** Ticket TTL in ms (default: `SSE_TICKET_TTL_MS` from api-core). */
|
|
70
71
|
ticketTtlMs?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Ticket + stream-ownership backend. Defaults to an in-memory store (correct
|
|
74
|
+
* single-replica only). Inject `createEnvRedisTicketStore()` (or a bespoke
|
|
75
|
+
* `SSETicketStore`) so ticket mint/redeem AND stream ownership work across
|
|
76
|
+
* horizontally-scaled pods and — critically — so the platform stream producer
|
|
77
|
+
* (a different service) can bind ownership that this service reads.
|
|
78
|
+
*/
|
|
79
|
+
ticketStore?: SSETicketStore;
|
|
80
|
+
/**
|
|
81
|
+
* TTL for a stream-ownership binding (default: `SSE_STREAM_OWNER_TTL_MS` env or
|
|
82
|
+
* 1h). Long enough to outlive a build; the producer re-binds as needed.
|
|
83
|
+
*/
|
|
84
|
+
streamOwnerTtlMs?: number;
|
|
71
85
|
}
|
|
72
86
|
/**
|
|
73
87
|
* A build-log stream subject id: either a dashed UUID (api-server's `uuid()`)
|
|
@@ -84,7 +98,7 @@ export type CreateTicketResult = {
|
|
|
84
98
|
ticket: string;
|
|
85
99
|
} | {
|
|
86
100
|
ok: false;
|
|
87
|
-
reason: 'org-limit' | 'capacity';
|
|
101
|
+
reason: 'org-limit' | 'capacity' | 'forbidden';
|
|
88
102
|
};
|
|
89
103
|
/**
|
|
90
104
|
* SSE Manager statistics
|
|
@@ -118,16 +132,17 @@ export declare class SSEManager {
|
|
|
118
132
|
* map mirrors `clients[].orgId` counts at all times. Orgs reach zero are
|
|
119
133
|
* deleted to keep the map bounded. */
|
|
120
134
|
private orgClientCounts;
|
|
121
|
-
/**
|
|
122
|
-
* `maxTotalTickets` / `maxTicketsPerOrg
|
|
123
|
-
* validated
|
|
124
|
-
private
|
|
135
|
+
/** Ticket + stream-ownership backend (in-memory default; Redis for multi-pod).
|
|
136
|
+
* Bounded by `maxTotalTickets` / `maxTicketsPerOrg`, enforced in createTicket;
|
|
137
|
+
* expiry is validated at consume time and swept on the cleanup interval. */
|
|
138
|
+
private readonly ticketStore;
|
|
125
139
|
private readonly maxClientsPerRequest;
|
|
126
140
|
private readonly maxTotalClients;
|
|
127
141
|
private readonly maxClientsPerOrg;
|
|
128
142
|
private readonly maxTotalTickets;
|
|
129
143
|
private readonly maxTicketsPerOrg;
|
|
130
144
|
private readonly ticketTtlMs;
|
|
145
|
+
private readonly streamOwnerTtlMs;
|
|
131
146
|
private readonly clientTimeoutMs;
|
|
132
147
|
private cleanupInterval;
|
|
133
148
|
constructor(options?: SSEManagerOptions);
|
|
@@ -142,8 +157,28 @@ export declare class SSEManager {
|
|
|
142
157
|
* expected to have already format-validated against {@link SSE_REQUEST_ID_RE}.
|
|
143
158
|
*/
|
|
144
159
|
private static normalizeRequestId;
|
|
145
|
-
/**
|
|
146
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Canonical form of an orgId for ownership equality checks: trimmed and
|
|
162
|
+
* lowercased. The stream PRODUCER (bindStreamOwner) and the ticket-minting
|
|
163
|
+
* CONSUMER (createTicket) run in different services, so a casing / whitespace
|
|
164
|
+
* drift between how each renders the same org would otherwise make the owner
|
|
165
|
+
* comparison in {@link createTicket} false-`forbidden` the real owner.
|
|
166
|
+
* Normalizing both sides — mirroring {@link normalizeRequestId} — closes that.
|
|
167
|
+
*/
|
|
168
|
+
private static normalizeOrgId;
|
|
169
|
+
/**
|
|
170
|
+
* Record the org that OWNS a stream subject. The stream PRODUCER calls this
|
|
171
|
+
* when it creates a build-log stream so that ticket minting can assert the
|
|
172
|
+
* caller's org owns the subject (see {@link createTicket}). Backed by the
|
|
173
|
+
* injected ticket store, so when a Redis store is wired the platform producer
|
|
174
|
+
* (a different service sharing the same Redis) can bind ownership that this
|
|
175
|
+
* service reads — closing the cross-tenant attach gap where any org could mint
|
|
176
|
+
* a ticket for a guessed requestId.
|
|
177
|
+
*
|
|
178
|
+
* @param requestId - The stream subject (format-validated by the caller).
|
|
179
|
+
* @param orgId - The owning org (normalized internally via normalizeOrgId).
|
|
180
|
+
*/
|
|
181
|
+
bindStreamOwner(requestId: string, orgId: string): Promise<void>;
|
|
147
182
|
/**
|
|
148
183
|
* Mint a short-lived, single-use SSE ticket bound to `orgId` AND a specific
|
|
149
184
|
* stream subject (`requestId`). Clients POST to obtain one (JWT-authenticated)
|
|
@@ -152,16 +187,23 @@ export declare class SSEManager {
|
|
|
152
187
|
* ticket can then only be consumed to open that one subject's stream — see
|
|
153
188
|
* {@link consumeTicket} — which is what enforces per-subject authorization.
|
|
154
189
|
*
|
|
190
|
+
* ORG-OWNERSHIP: if a stream owner has been bound for this subject (via
|
|
191
|
+
* {@link bindStreamOwner}) and it is a DIFFERENT org, minting is refused
|
|
192
|
+
* (`reason: 'forbidden'`) — an org cannot mint a ticket for another org's
|
|
193
|
+
* stream even if it guesses the requestId. When no owner is bound (producer
|
|
194
|
+
* wiring not yet present), minting falls back to binding the ticket to the
|
|
195
|
+
* caller's own org, preserving current behavior.
|
|
196
|
+
*
|
|
155
197
|
* Bounded by `maxTotalTickets` (process-wide) and `maxTicketsPerOrg`
|
|
156
198
|
* (per-tenant). Mirrors the message-service notifications ticket store.
|
|
157
199
|
*
|
|
158
|
-
* @param orgId - Owning org (
|
|
200
|
+
* @param orgId - Owning org (normalized internally via normalizeOrgId).
|
|
159
201
|
* @param requestId - The build-log stream subject this ticket authorizes.
|
|
160
202
|
* The caller must have format-validated it (see {@link SSE_REQUEST_ID_RE}).
|
|
161
203
|
* @returns `{ ok: true, ticket }` on success, or `{ ok: false, reason }`
|
|
162
|
-
* where reason is `'org-limit'`
|
|
204
|
+
* where reason is `'org-limit'`, `'capacity'`, or `'forbidden'`.
|
|
163
205
|
*/
|
|
164
|
-
createTicket(orgId: string, requestId: string): CreateTicketResult
|
|
206
|
+
createTicket(orgId: string, requestId: string): Promise<CreateTicketResult>;
|
|
165
207
|
/**
|
|
166
208
|
* Validate and CONSUME a ticket for a specific stream subject. Single-use:
|
|
167
209
|
* the ticket is deleted whether or not it turns out to be valid, so a replay
|
|
@@ -180,9 +222,9 @@ export declare class SSEManager {
|
|
|
180
222
|
* @param requestId - The stream subject from the URL path (`:requestId`),
|
|
181
223
|
* already format-validated by the caller.
|
|
182
224
|
*/
|
|
183
|
-
consumeTicket(ticketId: string, requestId: string): {
|
|
225
|
+
consumeTicket(ticketId: string, requestId: string): Promise<{
|
|
184
226
|
orgId: string;
|
|
185
|
-
} | null
|
|
227
|
+
} | null>;
|
|
186
228
|
/**
|
|
187
229
|
* Adds a client to the SSE manager
|
|
188
230
|
*
|
|
@@ -260,7 +302,7 @@ export declare class SSEManager {
|
|
|
260
302
|
query?: {
|
|
261
303
|
ticket?: unknown;
|
|
262
304
|
};
|
|
263
|
-
}, res: Response) => void
|
|
305
|
+
}, res: Response) => Promise<void>;
|
|
264
306
|
/**
|
|
265
307
|
* Start periodic cleanup of stale connections
|
|
266
308
|
*/
|