@getstrata/core 0.5.83 → 0.5.86
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 +12 -0
- package/README.md +1 -1
- package/dist/core/auth/sessionCookie.d.ts +9 -2
- package/dist/core/events/eventBus.d.ts +2 -1
- package/dist/core/events/index.d.ts +1 -1
- package/dist/entries/auth/sessionCookie.js +11 -2
- package/dist/entries/auth/sessionGuard.js +10 -2
- package/dist/entries/jobs/dispatchWebhookJob.js +13 -9
- package/dist/entries/openapi/generator.js +6 -0
- package/dist/index.js +12 -4
- package/dist/modules/webhook/dispatchWebhookJob.d.ts +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.86
|
|
4
|
+
|
|
5
|
+
- `createSessionCookieDetails()` returns the HMAC session header plus `issuedAt` / `ttlSeconds` so apps can persist Jetstream browser-session rows without changing the cookie format.
|
|
6
|
+
|
|
7
|
+
## 0.5.85
|
|
8
|
+
|
|
9
|
+
- `applicationRegistry` prefers the latest `Symbol.for("@getstrata/applicationContext")` on `globalThis` so a stale module-local context cannot hide the bootstrapped app after `build:framework`.
|
|
10
|
+
|
|
11
|
+
## 0.5.84
|
|
12
|
+
|
|
13
|
+
- `eventBus` is a `Symbol.for("@getstrata/eventBus")` process singleton so model writes from a built `@getstrata/core/database/baseRepository` bundle reach app listeners that imported a different copy of `@getstrata/core/events` (GitHub Actions `build:framework` before tests).
|
|
14
|
+
|
|
3
15
|
## 0.5.83
|
|
4
16
|
|
|
5
17
|
- `MEMBER_ABILITIES` includes `auth:tokens:delete` so Jetstream-style personal access token revoke works for members (`DELETE /auth/tokens/:id`). HTML `/account/tokens/:id/revoke` was already authenticated-only.
|
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ import type { Migration } from "@getstrata/core/database/migrations/types";
|
|
|
82
82
|
Package name: **`@getstrata/core`** (npm org [`@getstrata`](https://www.npmjs.com/org/getstrata)).
|
|
83
83
|
|
|
84
84
|
1. Add `NPM_TOKEN` to GitHub repository secrets.
|
|
85
|
-
2. Tag a release: `git tag v0.5.
|
|
85
|
+
2. Tag a release: `git tag v0.5.86 && git push origin v0.5.86`
|
|
86
86
|
3. [Release workflow](../../.github/workflows/release.yml) builds and runs `npm publish --access public`.
|
|
87
87
|
|
|
88
88
|
Previously published as `@eyk-workhub/framework@0.1.0`, deprecated in favor of this package.
|
|
@@ -14,7 +14,14 @@ declare function sessionRememberTtlSeconds(): number;
|
|
|
14
14
|
declare function readSession(request: Request): SignedSession | null;
|
|
15
15
|
declare function readSessionUserId(request: Request): number | null;
|
|
16
16
|
declare function isSessionInvalidated(issuedAt: number, validAfter: Date | string | null | undefined): boolean;
|
|
17
|
+
interface SessionCookieDetails {
|
|
18
|
+
header: string;
|
|
19
|
+
userId: number;
|
|
20
|
+
issuedAt: number;
|
|
21
|
+
ttlSeconds: number;
|
|
22
|
+
}
|
|
23
|
+
declare function createSessionCookieDetails(userId: number, options?: CreateSessionCookieOptions): SessionCookieDetails;
|
|
17
24
|
declare function createSessionCookie(userId: number, options?: CreateSessionCookieOptions): string;
|
|
18
25
|
declare function clearSessionCookie(): string;
|
|
19
|
-
export type { CreateSessionCookieOptions, SignedSession };
|
|
20
|
-
export { clearSessionCookie, createSessionCookie, isSessionInvalidated, readSession, readSessionUserId, SESSION_COOKIE, SESSION_REMEMBER_TTL_SECONDS, SESSION_TTL_SECONDS, sessionCookieName, sessionRememberTtlSeconds, sessionTtlSeconds, };
|
|
26
|
+
export type { CreateSessionCookieOptions, SessionCookieDetails, SignedSession };
|
|
27
|
+
export { clearSessionCookie, createSessionCookie, createSessionCookieDetails, isSessionInvalidated, readSession, readSessionUserId, SESSION_COOKIE, SESSION_REMEMBER_TTL_SECONDS, SESSION_TTL_SECONDS, sessionCookieName, sessionRememberTtlSeconds, sessionTtlSeconds, };
|
|
@@ -5,6 +5,7 @@ declare class EventBus {
|
|
|
5
5
|
listen(event: string, listener: EventListener): () => void;
|
|
6
6
|
dispatch(event: string, payload: unknown): Promise<void>;
|
|
7
7
|
}
|
|
8
|
+
declare function readSharedEventBus(): EventBus;
|
|
8
9
|
declare const eventBus: EventBus;
|
|
9
10
|
export type { EventListener };
|
|
10
|
-
export { EventBus, eventBus };
|
|
11
|
+
export { EventBus, eventBus, readSharedEventBus };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export type { EventListener } from "./eventBus";
|
|
2
|
-
export { EventBus, eventBus } from "./eventBus";
|
|
2
|
+
export { EventBus, eventBus, readSharedEventBus } from "./eventBus";
|
|
3
3
|
declare function modelEventName(tableName: string, action: string): string;
|
|
4
4
|
export { modelEventName };
|
|
@@ -149,12 +149,20 @@ function isSessionInvalidated(issuedAt, validAfter) {
|
|
|
149
149
|
}
|
|
150
150
|
return issuedAt < timestamp;
|
|
151
151
|
}
|
|
152
|
-
function
|
|
152
|
+
function createSessionCookieDetails(userId, options = {}) {
|
|
153
153
|
const issuedAt = Date.now();
|
|
154
154
|
const ttlSeconds = options.remember ? sessionRememberTtlSeconds() : sessionTtlSeconds();
|
|
155
155
|
const value = options.remember ? signSession(userId, issuedAt, ttlSeconds) : signSession(userId, issuedAt);
|
|
156
156
|
const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
|
|
157
|
-
return
|
|
157
|
+
return {
|
|
158
|
+
header: `${sessionCookieName()}=${encodeURIComponent(value)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${ttlSeconds}${secure}`,
|
|
159
|
+
userId,
|
|
160
|
+
issuedAt,
|
|
161
|
+
ttlSeconds
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function createSessionCookie(userId, options = {}) {
|
|
165
|
+
return createSessionCookieDetails(userId, options).header;
|
|
158
166
|
}
|
|
159
167
|
function clearSessionCookie() {
|
|
160
168
|
const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
|
|
@@ -166,6 +174,7 @@ export {
|
|
|
166
174
|
SESSION_TTL_SECONDS,
|
|
167
175
|
clearSessionCookie,
|
|
168
176
|
createSessionCookie,
|
|
177
|
+
createSessionCookieDetails,
|
|
169
178
|
isSessionInvalidated,
|
|
170
179
|
readSession,
|
|
171
180
|
readSessionUserId,
|
|
@@ -234,12 +234,20 @@ function isSessionInvalidated(issuedAt, validAfter) {
|
|
|
234
234
|
}
|
|
235
235
|
return issuedAt < timestamp;
|
|
236
236
|
}
|
|
237
|
-
function
|
|
237
|
+
function createSessionCookieDetails(userId, options = {}) {
|
|
238
238
|
const issuedAt = Date.now();
|
|
239
239
|
const ttlSeconds = options.remember ? sessionRememberTtlSeconds() : sessionTtlSeconds();
|
|
240
240
|
const value = options.remember ? signSession(userId, issuedAt, ttlSeconds) : signSession(userId, issuedAt);
|
|
241
241
|
const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
|
|
242
|
-
return
|
|
242
|
+
return {
|
|
243
|
+
header: `${sessionCookieName()}=${encodeURIComponent(value)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${ttlSeconds}${secure}`,
|
|
244
|
+
userId,
|
|
245
|
+
issuedAt,
|
|
246
|
+
ttlSeconds
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function createSessionCookie(userId, options = {}) {
|
|
250
|
+
return createSessionCookieDetails(userId, options).header;
|
|
243
251
|
}
|
|
244
252
|
function clearSessionCookie() {
|
|
245
253
|
const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
|
|
@@ -14,8 +14,9 @@ class DispatchWebhookJob extends Job {
|
|
|
14
14
|
maxAttempts = 3;
|
|
15
15
|
backoffMs = 2000;
|
|
16
16
|
async handle(payload) {
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
const tenantId = Number(payload.tenantId);
|
|
18
|
+
const tenant = await resolveTenant(tenantId) ?? {
|
|
19
|
+
id: tenantId,
|
|
19
20
|
slug: "job",
|
|
20
21
|
plan: "free",
|
|
21
22
|
region: "eu"
|
|
@@ -26,13 +27,16 @@ class DispatchWebhookJob extends Job {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
async deliver(payload) {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
const queuedUrl = typeof payload.url === "string" ? payload.url : "";
|
|
31
|
+
const queuedSecret = typeof payload.secret === "string" ? payload.secret : "";
|
|
32
|
+
const queued = queuedUrl !== "" && queuedSecret !== "" ? { id: payload.webhookId, url: queuedUrl, secret: queuedSecret } : null;
|
|
33
|
+
const rows = queued ? [] : await db`
|
|
34
|
+
SELECT id, url, secret
|
|
35
|
+
FROM webhook
|
|
36
|
+
WHERE id = ${payload.webhookId} AND active = TRUE
|
|
37
|
+
LIMIT 1
|
|
38
|
+
`;
|
|
39
|
+
const webhook = queued ?? rows[0];
|
|
36
40
|
if (!webhook) {
|
|
37
41
|
return;
|
|
38
42
|
}
|
|
@@ -66,10 +66,16 @@ var PUBLIC_ROUTE_DESCRIPTIONS = {
|
|
|
66
66
|
"POST /auth/tokens": "Create API token",
|
|
67
67
|
"DELETE /auth/tokens/:id": "Revoke API token",
|
|
68
68
|
"GET /users/me/export": "GDPR export of user data",
|
|
69
|
+
"GET /users/me/current-organization": "Current Jetstream organization",
|
|
70
|
+
"PUT /users/me/current-organization": "Switch current Jetstream organization",
|
|
71
|
+
"GET /users/me/invitations": "List pending team invitations for the signed-in email",
|
|
72
|
+
"POST /users/me/invitations/:id/accept": "Accept a pending team invitation",
|
|
73
|
+
"DELETE /users/me/invitations/:id": "Decline a pending team invitation",
|
|
69
74
|
"DELETE /users/me": "GDPR account erasure (anonymize user, revoke tokens)",
|
|
70
75
|
"GET /organizations": "List organizations",
|
|
71
76
|
"POST /organizations": "Create organization",
|
|
72
77
|
"GET /organizations/:id/members": "List organization members",
|
|
78
|
+
"POST /organizations/:id/invitations/:invitationId/resend": "Resend a pending team invitation and rotate its token",
|
|
73
79
|
"GET /projects": "List projects",
|
|
74
80
|
"POST /projects": "Create project",
|
|
75
81
|
"GET /tasks": "List tasks",
|
package/dist/index.js
CHANGED
|
@@ -588,12 +588,10 @@ var appLogger = new Logger("app");
|
|
|
588
588
|
var APPLICATION_CONTEXT_KEY = Symbol.for("@getstrata/applicationContext");
|
|
589
589
|
var activeContext;
|
|
590
590
|
function readStoredApplicationContext() {
|
|
591
|
-
if (activeContext) {
|
|
592
|
-
return activeContext;
|
|
593
|
-
}
|
|
594
591
|
const globalContext = globalThis[APPLICATION_CONTEXT_KEY];
|
|
595
592
|
if (globalContext) {
|
|
596
593
|
activeContext = globalContext;
|
|
594
|
+
return activeContext;
|
|
597
595
|
}
|
|
598
596
|
return activeContext;
|
|
599
597
|
}
|
|
@@ -1532,7 +1530,17 @@ class EventBus {
|
|
|
1532
1530
|
}
|
|
1533
1531
|
}
|
|
1534
1532
|
}
|
|
1535
|
-
var
|
|
1533
|
+
var EVENT_BUS_KEY = Symbol.for("@getstrata/eventBus");
|
|
1534
|
+
function readSharedEventBus() {
|
|
1535
|
+
const globalBus = globalThis[EVENT_BUS_KEY];
|
|
1536
|
+
if (globalBus) {
|
|
1537
|
+
return globalBus;
|
|
1538
|
+
}
|
|
1539
|
+
const bus = new EventBus;
|
|
1540
|
+
globalThis[EVENT_BUS_KEY] = bus;
|
|
1541
|
+
return bus;
|
|
1542
|
+
}
|
|
1543
|
+
var eventBus = readSharedEventBus();
|
|
1536
1544
|
|
|
1537
1545
|
// ../../src/core/events/index.ts
|
|
1538
1546
|
function modelEventName(tableName, action) {
|