anbaric-state-machine 1.18.0 → 1.19.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anbaric-state-machine",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "This is the state machine library that one can use to add state management to an application. It will, by default, be locally runnable but can deployed to the Anbaric Cloud via the CLI",
5
5
  "license": "MIT",
6
6
  "author": "chris@anbaric.ai",
@@ -15,8 +15,8 @@
15
15
  "typescript": "^7.0.2"
16
16
  },
17
17
  "dependencies": {
18
- "anbaric-impl-cloud": "^1.18.0",
19
- "anbaric-tsapi": "^1.18.0"
18
+ "anbaric-impl-cloud": "^1.19.0",
19
+ "anbaric-tsapi": "^1.19.0"
20
20
  },
21
21
  "files": [
22
22
  "src"
@@ -65,7 +65,7 @@ class StateMachine {
65
65
  actions: state.actions.map(action => ({
66
66
  name: action.name,
67
67
  description: action.description,
68
- actor: { id: action.actor.id, type: action.actor.type, role: action.actor.role },
68
+ actor: { id: action.actor.id, type: action.actor.type, roles: action.actor.roles },
69
69
  })),
70
70
  transitions: state.transitions.map(transition => ({ to: transition.to })),
71
71
  })),
@@ -4,11 +4,11 @@ class Code implements Actor {
4
4
 
5
5
  readonly type = "CODE" as const;
6
6
  readonly id : string;
7
- readonly role : string;
7
+ readonly roles : Array<string>;
8
8
 
9
- constructor(id : string, role : string = "code") {
9
+ constructor(id : string, roles : Array<string> | string = "code") {
10
10
  this.id = id;
11
- this.role = role;
11
+ this.roles = typeof roles === "string" ? [roles] : roles;
12
12
  }
13
13
 
14
14
  }
@@ -1,18 +1,30 @@
1
- import {Actor} from "anbaric-tsapi";
1
+ import {IncomingMessage} from "node:http";
2
+ import {Actor, SessionResolver} from "anbaric-tsapi";
3
+ import {SessionResolverFactory} from "../sessions/SessionResolverFactory";
4
+ import {sessionCookie} from "../sessions/sessionCookie";
2
5
 
3
6
  class Human implements Actor {
4
7
 
5
8
  readonly type = "HUMAN" as const;
6
9
  readonly id : string;
7
- readonly role : string;
10
+ readonly roles : Array<string>;
8
11
 
9
- constructor(id : string, role : string) {
12
+ constructor(id : string, roles : Array<string> | string) {
10
13
  this.id = id;
11
- this.role = role;
14
+ this.roles = typeof roles === "string" ? [roles] : roles;
12
15
  }
13
16
 
14
- static createFromSession() {
15
-
17
+ /* Resolves the browser user behind a request to an app page into a Human,
18
+ for auditing. Takes the platform session - either the anbaric_session
19
+ token, or the incoming request to read the cookie from - and asks the
20
+ platform to verify it (an app cannot: the signing secret is platform
21
+ only). */
22
+ static async fromSession(source : string | IncomingMessage,
23
+ resolver : SessionResolver = SessionResolverFactory.instance()) : Promise<Human> {
24
+ const token = typeof source === "string" ? source : sessionCookie(source);
25
+ const session = token ? await resolver.resolve(token) : undefined;
26
+ if (!session) throw new Error("Could not resolve the session");
27
+ return new Human(session.id, session.roles);
16
28
  }
17
29
 
18
30
  }
package/src/index.ts CHANGED
@@ -11,3 +11,6 @@ export * from "./scheduling/InMemoryQueue";
11
11
  export * from "./scheduling/PullConsumer";
12
12
  export * from "./scheduling/ConsumerFactory";
13
13
  export * from "./scheduling/QueueFactory";
14
+ export * from "./sessions/InMemorySessionResolver";
15
+ export * from "./sessions/SessionResolverFactory";
16
+ export * from "./sessions/sessionCookie";
@@ -0,0 +1,20 @@
1
+ import {ResolvedSession, SessionResolver} from "anbaric-tsapi";
2
+
3
+ /* A resolver backed by a seeded token->session map, for local runs and tests.
4
+ Empty by default, so an unknown token resolves to undefined. */
5
+ class InMemorySessionResolver implements SessionResolver {
6
+
7
+ private sessions = new Map<string, ResolvedSession>();
8
+
9
+ seed(sessionToken : string, session : ResolvedSession) : this {
10
+ this.sessions.set(sessionToken, session);
11
+ return this;
12
+ }
13
+
14
+ async resolve(sessionToken : string) : Promise<ResolvedSession | undefined> {
15
+ return this.sessions.get(sessionToken);
16
+ }
17
+
18
+ }
19
+
20
+ export { InMemorySessionResolver }
@@ -0,0 +1,16 @@
1
+ import {SessionResolver} from "anbaric-tsapi";
2
+ import {CloudSessionResolver} from "anbaric-impl-cloud";
3
+ import {InMemorySessionResolver} from "./InMemorySessionResolver";
4
+
5
+ const SessionResolverFactory = {
6
+ instance() : SessionResolver {
7
+ switch (process.env.ANBARIC_SESSION_RESOLVER_TYPE) {
8
+ case "cloud":
9
+ return new CloudSessionResolver();
10
+ default:
11
+ return new InMemorySessionResolver();
12
+ }
13
+ }
14
+ }
15
+
16
+ export { SessionResolverFactory };
@@ -0,0 +1,16 @@
1
+ import {IncomingMessage} from "node:http";
2
+
3
+ const SESSION_COOKIE = "anbaric_session";
4
+
5
+ /* Reads the anbaric_session cookie value from an incoming request (or a raw
6
+ Cookie header string). Returns undefined when the cookie is absent. */
7
+ const sessionCookie = (source : IncomingMessage | string) : string | undefined => {
8
+ const header = typeof source === "string" ? source : String(source.headers.cookie ?? "");
9
+ for (const cookie of header.split(";")) {
10
+ const [name, ...value] = cookie.trim().split("=");
11
+ if (name === SESSION_COOKIE) return value.join("=");
12
+ }
13
+ return undefined;
14
+ };
15
+
16
+ export { sessionCookie, SESSION_COOKIE }