@arcote.tech/arc 0.5.2 → 0.5.5
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/adapters/auth-adapter.d.ts +11 -0
- package/dist/adapters/event-publisher.d.ts +5 -0
- package/dist/adapters/event-wire.d.ts +3 -0
- package/dist/context-element/aggregate/index.d.ts +1 -1
- package/dist/context-element/event/index.d.ts +1 -1
- package/dist/context-element/event/instance.d.ts +19 -0
- package/dist/context-element/listener/listener.d.ts +10 -4
- package/dist/index.js +30 -29
- package/package.json +1 -1
|
@@ -19,6 +19,17 @@ export declare class AuthAdapter {
|
|
|
19
19
|
* Auto-persists to localStorage when available.
|
|
20
20
|
*/
|
|
21
21
|
setToken(token: string | null, scope?: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Set an already-decoded token for a scope, bypassing JWT parsing.
|
|
24
|
+
*
|
|
25
|
+
* Used by listener auth reconstruction: events carry decoded auth context
|
|
26
|
+
* (tokenName + params) which has been validated at emit time. This method
|
|
27
|
+
* restores that context into a fresh AuthAdapter without requiring the
|
|
28
|
+
* original raw JWT.
|
|
29
|
+
*
|
|
30
|
+
* Does NOT touch localStorage — purely in-memory restoration.
|
|
31
|
+
*/
|
|
32
|
+
setDecoded(decoded: DecodedToken, scope?: string): void;
|
|
22
33
|
/**
|
|
23
34
|
* Load all persisted scope tokens from localStorage.
|
|
24
35
|
* Call once on app init before any queries.
|
|
@@ -27,6 +27,11 @@ export interface StoredEvent {
|
|
|
27
27
|
type: string;
|
|
28
28
|
payload: string;
|
|
29
29
|
createdAt: string;
|
|
30
|
+
/**
|
|
31
|
+
* JSON-serialized EventAuthContext snapshot from the mutation that emitted
|
|
32
|
+
* this event. Null for system events / cron / seed (no active auth scope).
|
|
33
|
+
*/
|
|
34
|
+
authContext: string | null;
|
|
30
35
|
}
|
|
31
36
|
/** Event tag record in the event_tags table */
|
|
32
37
|
export interface StoredEventTag {
|
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
* - Handles reconnection and sync state
|
|
8
8
|
* - Manages per-scope token caching
|
|
9
9
|
*/
|
|
10
|
+
import type { EventAuthContext } from "../context-element/event/instance";
|
|
10
11
|
export interface SyncableEvent {
|
|
11
12
|
localId: string;
|
|
12
13
|
type: string;
|
|
13
14
|
payload: any;
|
|
14
15
|
createdAt: string;
|
|
16
|
+
authContext: EventAuthContext | null;
|
|
15
17
|
}
|
|
16
18
|
export interface ReceivedEvent {
|
|
17
19
|
localId: string;
|
|
@@ -20,6 +22,7 @@ export interface ReceivedEvent {
|
|
|
20
22
|
payload: any;
|
|
21
23
|
createdAt: string;
|
|
22
24
|
clientId: string;
|
|
25
|
+
authContext: EventAuthContext | null;
|
|
23
26
|
}
|
|
24
27
|
import type { ContextDescriptor } from "../model/context-accessor";
|
|
25
28
|
type EventWireState = "disconnected" | "connecting" | "connected";
|
|
@@ -11,5 +11,5 @@ export { aggregate } from "./aggregate-builder";
|
|
|
11
11
|
export { ArcAggregateElement, type AggregateConstructor, type AggregateConstructorAny, type AggregateData, type AggregateInstanceCtx, type AggregateRow, } from "./aggregate-element";
|
|
12
12
|
export { AggregateBase } from "./aggregate-base";
|
|
13
13
|
export type { AggregateCronMethodEntry, AggregateEventEntry, AggregateEventHandler, AggregateMutateMethodContext, AggregateMutateMethodEntry, AggregateQueryContext, AggregateQueryMethodEntry, AggregateStaticConfig, } from "./aggregate-data";
|
|
14
|
-
export type { ArcEventPayload } from "../event/instance";
|
|
14
|
+
export type { ArcEventPayload, EventAuthContext, EventMetadata } from "../event/instance";
|
|
15
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ArcEvent, event, type ArcEventAny } from "./event";
|
|
2
2
|
export type { ArcEventData } from "./event-data";
|
|
3
|
-
export type { ArcEventInstance, ArcEventPayload, EventMetadata, } from "./instance";
|
|
3
|
+
export type { ArcEventInstance, ArcEventPayload, EventAuthContext, EventMetadata, } from "./instance";
|
|
4
4
|
export type { ArcObjectAny, ArcRawShape } from "../../elements/object";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import type { $type } from "../../utils/types/get-type";
|
|
2
2
|
import type { ArcEventAny } from "./event";
|
|
3
|
+
/**
|
|
4
|
+
* Auth context captured at event emission time.
|
|
5
|
+
* Stored alongside the event in the event store so async listeners can
|
|
6
|
+
* reconstruct the original protection scope without losing security guarantees.
|
|
7
|
+
*
|
|
8
|
+
* Trust model: the event store is trusted (same threat surface as the
|
|
9
|
+
* aggregate state DB). No cryptographic re-verification — payload is
|
|
10
|
+
* authoritative because only authorized mutations could have written it.
|
|
11
|
+
*/
|
|
12
|
+
export type EventAuthContext = {
|
|
13
|
+
tokenName: string;
|
|
14
|
+
params: Record<string, any>;
|
|
15
|
+
};
|
|
3
16
|
/**
|
|
4
17
|
* Event metadata attached to every event instance
|
|
5
18
|
*/
|
|
6
19
|
export type EventMetadata = {
|
|
7
20
|
id: string;
|
|
8
21
|
createdAt: Date;
|
|
22
|
+
/**
|
|
23
|
+
* Auth context that authorized the mutation which emitted this event.
|
|
24
|
+
* `null` for system events emitted without an active auth scope —
|
|
25
|
+
* listeners with `protectBy()` will deny access for null-auth events.
|
|
26
|
+
*/
|
|
27
|
+
authContext: EventAuthContext | null;
|
|
9
28
|
};
|
|
10
29
|
/**
|
|
11
30
|
* Event instance type - represents an actual event occurrence
|
|
@@ -77,11 +77,17 @@ export declare class ArcListener<const Data extends ArcListenerData> extends Arc
|
|
|
77
77
|
init(environment: ArcEnvironment, adapters: ModelAdapters): Promise<void>;
|
|
78
78
|
private handleEvent;
|
|
79
79
|
/**
|
|
80
|
-
* Build scoped adapters
|
|
80
|
+
* Build scoped adapters for the listener handler.
|
|
81
81
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
82
|
+
* Sync listeners run inline with the originating mutation and inherit the
|
|
83
|
+
* caller's auth context directly. Async listeners run after the mutation
|
|
84
|
+
* completes and lose request scope — they reconstruct auth from the
|
|
85
|
+
* `authContext` snapshotted into the event at emit time
|
|
86
|
+
* (see aggregate-element.ts emit handler).
|
|
87
|
+
*
|
|
88
|
+
* Events with `authContext: null` (system events / cron / seed) leave the
|
|
89
|
+
* adapters without auth — listeners with `protectBy()` will deny access
|
|
90
|
+
* downstream at the protection layer.
|
|
85
91
|
*/
|
|
86
92
|
private buildScopedAdapters;
|
|
87
93
|
destroy(): void;
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,9 @@ class AuthAdapter {
|
|
|
50
50
|
localStorage.removeItem(TOKEN_PREFIX + scope);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
setDecoded(decoded, scope = "default") {
|
|
54
|
+
this.scopes.set(scope, { raw: "", decoded });
|
|
55
|
+
}
|
|
53
56
|
loadPersisted() {
|
|
54
57
|
if (!hasLocalStorage())
|
|
55
58
|
return;
|
|
@@ -325,7 +328,8 @@ class EventWire {
|
|
|
325
328
|
localId: e.localId,
|
|
326
329
|
type: e.type,
|
|
327
330
|
payload: e.payload,
|
|
328
|
-
createdAt: e.createdAt
|
|
331
|
+
createdAt: e.createdAt,
|
|
332
|
+
authContext: e.authContext
|
|
329
333
|
}))
|
|
330
334
|
}));
|
|
331
335
|
}
|
|
@@ -541,11 +545,13 @@ class LocalEventPublisher {
|
|
|
541
545
|
}
|
|
542
546
|
async publish(event) {
|
|
543
547
|
const allChanges = [];
|
|
548
|
+
const eventAuthContext = event.authContext ?? null;
|
|
544
549
|
const storedEvent = {
|
|
545
550
|
_id: event.id,
|
|
546
551
|
type: event.type,
|
|
547
552
|
payload: JSON.stringify(event.payload),
|
|
548
|
-
createdAt: event.createdAt.toISOString()
|
|
553
|
+
createdAt: event.createdAt.toISOString(),
|
|
554
|
+
authContext: eventAuthContext ? JSON.stringify(eventAuthContext) : null
|
|
549
555
|
};
|
|
550
556
|
allChanges.push({
|
|
551
557
|
store: EVENT_TABLES.events,
|
|
@@ -1775,11 +1781,14 @@ class ArcEvent extends ArcContextElement {
|
|
|
1775
1781
|
if (!adapters.eventPublisher) {
|
|
1776
1782
|
throw new Error(`Event "${this.data.name}" cannot be emitted: no eventPublisher adapter available`);
|
|
1777
1783
|
}
|
|
1784
|
+
const decoded = adapters.authAdapter?.getDecoded() ?? null;
|
|
1785
|
+
const authContext = decoded ? { tokenName: decoded.tokenName, params: decoded.params } : null;
|
|
1778
1786
|
const event = {
|
|
1779
1787
|
type: this.data.name,
|
|
1780
1788
|
payload,
|
|
1781
1789
|
id: this.eventId.generate(),
|
|
1782
|
-
createdAt: new Date
|
|
1790
|
+
createdAt: new Date,
|
|
1791
|
+
authContext
|
|
1783
1792
|
};
|
|
1784
1793
|
await adapters.eventPublisher.publish(event);
|
|
1785
1794
|
}
|
|
@@ -1997,11 +2006,14 @@ class AggregateBase {
|
|
|
1997
2006
|
if (!adapters.eventPublisher) {
|
|
1998
2007
|
throw new Error(`Cannot emit event "${arcEvent.name}": no eventPublisher adapter available`);
|
|
1999
2008
|
}
|
|
2009
|
+
const decoded = adapters.authAdapter?.getDecoded() ?? null;
|
|
2010
|
+
const authContext = decoded ? { tokenName: decoded.tokenName, params: decoded.params } : null;
|
|
2000
2011
|
const eventInstance = {
|
|
2001
2012
|
type: arcEvent.name,
|
|
2002
2013
|
payload,
|
|
2003
2014
|
id: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
2004
|
-
createdAt: new Date
|
|
2015
|
+
createdAt: new Date,
|
|
2016
|
+
authContext
|
|
2005
2017
|
};
|
|
2006
2018
|
await adapters.eventPublisher.publish(eventInstance);
|
|
2007
2019
|
}
|
|
@@ -2314,11 +2326,14 @@ class ArcAggregateElement extends ArcContextElement {
|
|
|
2314
2326
|
}
|
|
2315
2327
|
}
|
|
2316
2328
|
}
|
|
2329
|
+
const decoded = adapters.authAdapter?.getDecoded() ?? null;
|
|
2330
|
+
const authContext = decoded ? { tokenName: decoded.tokenName, params: decoded.params } : null;
|
|
2317
2331
|
const eventInstance = {
|
|
2318
2332
|
type: `${aggregateName}.${arcEvent.name}`,
|
|
2319
2333
|
payload,
|
|
2320
2334
|
id: `${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
2321
|
-
createdAt: new Date
|
|
2335
|
+
createdAt: new Date,
|
|
2336
|
+
authContext
|
|
2322
2337
|
};
|
|
2323
2338
|
await adapters.eventPublisher.publish(eventInstance);
|
|
2324
2339
|
}
|
|
@@ -2597,31 +2612,15 @@ class ArcListener extends ArcContextElement {
|
|
|
2597
2612
|
if (adapters.authAdapter?.isAuthenticated()) {
|
|
2598
2613
|
return adapters;
|
|
2599
2614
|
}
|
|
2600
|
-
const
|
|
2601
|
-
|
|
2602
|
-
...this.data.mutationElements
|
|
2603
|
-
];
|
|
2604
|
-
let tokenName = null;
|
|
2605
|
-
for (const element of allElements) {
|
|
2606
|
-
const protections = element.__aggregateProtections ?? element.data?.protections;
|
|
2607
|
-
if (protections?.length > 0) {
|
|
2608
|
-
tokenName = protections[0].token.name;
|
|
2609
|
-
break;
|
|
2610
|
-
}
|
|
2611
|
-
}
|
|
2612
|
-
if (!tokenName || !event2.payload) {
|
|
2615
|
+
const authContext = event2.authContext;
|
|
2616
|
+
if (!authContext) {
|
|
2613
2617
|
return adapters;
|
|
2614
2618
|
}
|
|
2615
2619
|
const scopedAuth = new AuthAdapter;
|
|
2616
|
-
scopedAuth.
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
tokenName,
|
|
2621
|
-
params: event2.payload
|
|
2622
|
-
}
|
|
2623
|
-
}]
|
|
2624
|
-
]);
|
|
2620
|
+
scopedAuth.setDecoded({
|
|
2621
|
+
tokenName: authContext.tokenName,
|
|
2622
|
+
params: authContext.params
|
|
2623
|
+
});
|
|
2625
2624
|
return { ...adapters, authAdapter: scopedAuth };
|
|
2626
2625
|
}
|
|
2627
2626
|
destroy() {
|
|
@@ -3059,7 +3058,8 @@ class ArcView extends ArcContextElement {
|
|
|
3059
3058
|
type: storedEvent.type,
|
|
3060
3059
|
payload,
|
|
3061
3060
|
id: storedEvent._id,
|
|
3062
|
-
createdAt: new Date(storedEvent.createdAt)
|
|
3061
|
+
createdAt: new Date(storedEvent.createdAt),
|
|
3062
|
+
authContext: storedEvent.authContext ? typeof storedEvent.authContext === "string" ? JSON.parse(storedEvent.authContext) : storedEvent.authContext : null
|
|
3063
3063
|
};
|
|
3064
3064
|
await handler(ctx, eventInstance);
|
|
3065
3065
|
}
|
|
@@ -4770,7 +4770,8 @@ class StreamingEventPublisher {
|
|
|
4770
4770
|
localId: event3.id,
|
|
4771
4771
|
type: event3.type,
|
|
4772
4772
|
payload: event3.payload,
|
|
4773
|
-
createdAt: event3.createdAt.toISOString()
|
|
4773
|
+
createdAt: event3.createdAt.toISOString(),
|
|
4774
|
+
authContext: event3.authContext ?? null
|
|
4774
4775
|
}
|
|
4775
4776
|
]);
|
|
4776
4777
|
}
|
package/package.json
CHANGED