@inventbuild/supamachine 0.3.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.
@@ -0,0 +1,177 @@
1
+ import { AuthEventType, AuthStateStatus } from "./constants";
2
+ import { reducer, setReducerLogLevel } from "./reducer";
3
+ import { createLogger } from "./logger";
4
+ const DEFAULT_LOAD_CONTEXT_TIMEOUT_MS = 10000;
5
+ const DEFAULT_INITIALIZE_APP_TIMEOUT_MS = 30000;
6
+ const DEFAULT_AUTHENTICATING_TIMEOUT_MS = 30000;
7
+ function computeAppState(state, mapState) {
8
+ if (state.status !== AuthStateStatus.AUTH_READY) {
9
+ return state;
10
+ }
11
+ if (mapState && state.context != null) {
12
+ const snapshot = {
13
+ status: state.status,
14
+ session: state.session,
15
+ context: state.context,
16
+ };
17
+ const base = mapState(snapshot);
18
+ return {
19
+ ...base,
20
+ session: state.session,
21
+ context: state.context,
22
+ };
23
+ }
24
+ return state;
25
+ }
26
+ export class SupamachineCore {
27
+ constructor(options) {
28
+ var _a, _b, _c, _d;
29
+ this.options = options;
30
+ this.state = {
31
+ status: AuthStateStatus.START,
32
+ context: null,
33
+ };
34
+ this.sessionForLoading = null;
35
+ this.authenticatingTimer = null;
36
+ this.listeners = new Set();
37
+ this.loadContextTimeoutMs =
38
+ (_a = options.loadContextTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_LOAD_CONTEXT_TIMEOUT_MS;
39
+ this.initializeAppTimeoutMs =
40
+ (_b = options.initializeAppTimeoutMs) !== null && _b !== void 0 ? _b : DEFAULT_INITIALIZE_APP_TIMEOUT_MS;
41
+ this.authenticatingTimeoutMs =
42
+ (_c = options.authenticatingTimeoutMs) !== null && _c !== void 0 ? _c : DEFAULT_AUTHENTICATING_TIMEOUT_MS;
43
+ const level = (_d = options.logLevel) !== null && _d !== void 0 ? _d : 2;
44
+ this.log = createLogger("core", level);
45
+ setReducerLogLevel(level);
46
+ }
47
+ setLogLevel(level) {
48
+ this.log = createLogger("core", level);
49
+ setReducerLogLevel(level);
50
+ }
51
+ async updateContext(updater) {
52
+ if (this.state.status !== AuthStateStatus.AUTH_READY) {
53
+ return;
54
+ }
55
+ const current = this.state.context;
56
+ if (current == null) {
57
+ return;
58
+ }
59
+ const next = await updater(current);
60
+ if (next !== current) {
61
+ this.state = { ...this.state, context: next };
62
+ this.emit();
63
+ }
64
+ }
65
+ beginAuth() {
66
+ this.dispatch({ type: AuthEventType.AUTH_INITIATED });
67
+ }
68
+ cancelAuth() {
69
+ this.dispatch({ type: AuthEventType.AUTH_CANCELLED });
70
+ }
71
+ dispatch(event) {
72
+ var _a;
73
+ const prevState = this.state;
74
+ this.state = reducer(this.state, event);
75
+ if (event.type === AuthEventType.AUTH_CHANGED) {
76
+ this.sessionForLoading = (_a = event.session) !== null && _a !== void 0 ? _a : null;
77
+ }
78
+ this.emit();
79
+ // AUTHENTICATING timeout management
80
+ if (this.state.status === AuthStateStatus.AUTHENTICATING &&
81
+ prevState.status !== AuthStateStatus.AUTHENTICATING) {
82
+ this.authenticatingTimer = setTimeout(() => {
83
+ this.log.warn("authenticating timeout — cancelling");
84
+ this.dispatch({ type: AuthEventType.AUTH_CANCELLED });
85
+ }, this.authenticatingTimeoutMs);
86
+ }
87
+ if (prevState.status === AuthStateStatus.AUTHENTICATING &&
88
+ this.state.status !== AuthStateStatus.AUTHENTICATING) {
89
+ if (this.authenticatingTimer) {
90
+ clearTimeout(this.authenticatingTimer);
91
+ this.authenticatingTimer = null;
92
+ }
93
+ }
94
+ if (this.state.status === AuthStateStatus.CONTEXT_LOADING &&
95
+ prevState.status !== AuthStateStatus.CONTEXT_LOADING) {
96
+ const session = this.sessionForLoading;
97
+ if (session && this.options.loadContext) {
98
+ this.log.debug("entered CONTEXT_LOADING, loading context");
99
+ this.loadContextWithTimeout(session);
100
+ }
101
+ else if (session && !this.options.loadContext) {
102
+ this.log.debug("no loadContext, resolving with empty context");
103
+ this.dispatch({
104
+ type: AuthEventType.CONTEXT_RESOLVED,
105
+ context: {},
106
+ });
107
+ }
108
+ }
109
+ else if (this.state.status === AuthStateStatus.SIGNED_OUT &&
110
+ prevState.status !== AuthStateStatus.SIGNED_OUT) {
111
+ this.sessionForLoading = null;
112
+ this.log.debug("signed out");
113
+ }
114
+ else if (this.state.status === AuthStateStatus.INITIALIZING &&
115
+ prevState.status === AuthStateStatus.CONTEXT_LOADING) {
116
+ this.log.debug("entered INITIALIZING, running initializeApp");
117
+ this.initializeAppWithTimeout();
118
+ }
119
+ }
120
+ async loadContextWithTimeout(session) {
121
+ const loadContext = this.options.loadContext;
122
+ if (!loadContext)
123
+ return;
124
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("loadContext timeout")), this.loadContextTimeoutMs));
125
+ try {
126
+ const context = await Promise.race([
127
+ loadContext(session),
128
+ timeoutPromise,
129
+ ]);
130
+ this.dispatch({
131
+ type: AuthEventType.CONTEXT_RESOLVED,
132
+ context,
133
+ });
134
+ }
135
+ catch (error) {
136
+ this.log.error("loadContext failed", error);
137
+ this.dispatch({
138
+ type: AuthEventType.ERROR_CONTEXT,
139
+ error: error instanceof Error ? error : new Error(String(error)),
140
+ });
141
+ }
142
+ }
143
+ async initializeAppWithTimeout() {
144
+ const { initializeApp } = this.options;
145
+ if (!initializeApp || this.state.status !== AuthStateStatus.INITIALIZING) {
146
+ return;
147
+ }
148
+ const { session, context } = this.state;
149
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("initializeApp timeout")), this.initializeAppTimeoutMs));
150
+ try {
151
+ await Promise.race([initializeApp({ session, context }), timeoutPromise]);
152
+ this.dispatch({ type: AuthEventType.INITIALIZED });
153
+ }
154
+ catch (error) {
155
+ this.log.error("initializeApp failed", error);
156
+ this.dispatch({
157
+ type: AuthEventType.ERROR_INITIALIZING,
158
+ error: error instanceof Error ? error : new Error(String(error)),
159
+ });
160
+ }
161
+ }
162
+ subscribe(fn) {
163
+ this.listeners.add(fn);
164
+ return () => this.listeners.delete(fn);
165
+ }
166
+ getSnapshot() {
167
+ return this.state;
168
+ }
169
+ getAppState() {
170
+ return computeAppState(this.state, this.options.mapState);
171
+ }
172
+ emit() {
173
+ const appState = computeAppState(this.state, this.options.mapState);
174
+ for (const l of this.listeners)
175
+ l(this.state, appState);
176
+ }
177
+ }
@@ -0,0 +1,42 @@
1
+ import type { Session } from "@supabase/supabase-js";
2
+ import { AuthStateStatus } from "./constants";
3
+ /** context is always present: null before load, C once loaded */
4
+ export type CoreState<C> = {
5
+ status: typeof AuthStateStatus.START;
6
+ context: null;
7
+ } | {
8
+ status: typeof AuthStateStatus.CHECKING_SESSION;
9
+ context: null;
10
+ } | {
11
+ status: typeof AuthStateStatus.AUTHENTICATING;
12
+ context: null;
13
+ } | {
14
+ status: typeof AuthStateStatus.ERROR_CHECKING_SESSION;
15
+ error: Error;
16
+ context: null;
17
+ } | {
18
+ status: typeof AuthStateStatus.SIGNED_OUT;
19
+ context: null;
20
+ } | {
21
+ status: typeof AuthStateStatus.CONTEXT_LOADING;
22
+ session: Session;
23
+ context: null;
24
+ } | {
25
+ status: typeof AuthStateStatus.ERROR_CONTEXT;
26
+ error: Error;
27
+ session: Session;
28
+ context: null;
29
+ } | {
30
+ status: typeof AuthStateStatus.INITIALIZING;
31
+ session: Session;
32
+ context: C;
33
+ } | {
34
+ status: typeof AuthStateStatus.ERROR_INITIALIZING;
35
+ error: Error;
36
+ session: Session;
37
+ context: C;
38
+ } | {
39
+ status: typeof AuthStateStatus.AUTH_READY;
40
+ session: Session;
41
+ context: C | null;
42
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ import type { Session } from "@supabase/supabase-js";
2
+ import type { CoreState } from "./states";
3
+ import { AuthStateStatus } from "./constants";
4
+ type DisallowedStatuses = typeof AuthStateStatus.START | typeof AuthStateStatus.CHECKING_SESSION | typeof AuthStateStatus.AUTHENTICATING | typeof AuthStateStatus.ERROR_CHECKING_SESSION | typeof AuthStateStatus.SIGNED_OUT | typeof AuthStateStatus.CONTEXT_LOADING | typeof AuthStateStatus.ERROR_CONTEXT | typeof AuthStateStatus.INITIALIZING | typeof AuthStateStatus.ERROR_INITIALIZING | typeof AuthStateStatus.AUTH_READY;
5
+ type CustomStateConstraint = {
6
+ status: Exclude<string, DisallowedStatuses>;
7
+ };
8
+ /** mapState is only called when we have context. Custom states inherit guaranteed context. */
9
+ type WithSessionContext<C, T> = T extends {
10
+ status: string;
11
+ } ? T & {
12
+ session: Session;
13
+ context: C;
14
+ } : never;
15
+ /** Snapshot passed to mapState. Context is guaranteed—mapState only runs when we have context to derive from. */
16
+ export type MapStateSnapshot<C> = {
17
+ status: typeof AuthStateStatus.AUTH_READY;
18
+ session: Session;
19
+ context: C;
20
+ };
21
+ /** When D is void (default), returns CoreState. When D is custom states, returns core states | (D with session+context added) */
22
+ export type AppState<C, D = void> = [D] extends [void] ? CoreState<C> : Exclude<CoreState<C>, {
23
+ status: typeof AuthStateStatus.AUTH_READY;
24
+ }> | WithSessionContext<C, D>;
25
+ /** Default actions provided when none are passed. signOut is always available. */
26
+ export type DefaultActions = {
27
+ signOut: () => void | Promise<unknown>;
28
+ };
29
+ /** User-provided auth actions (signIn, signOut, etc.). Merged with default signOut. */
30
+ export type SupamachineActions<A = Record<string, never>> = DefaultActions & A;
31
+ export interface SupamachineProviderProps<C, D extends CustomStateConstraint | void = void, A extends Record<string, (...args: any[]) => any> = Record<string, (...args: any[]) => any>> {
32
+ loadContext?: (session: Session) => Promise<C>;
33
+ initializeApp?: (snapshot: {
34
+ session: Session;
35
+ context: C;
36
+ }) => void | Promise<void>;
37
+ mapState?: [D] extends [void] ? never : (snapshot: MapStateSnapshot<C>) => D;
38
+ /** Auth actions to expose via useSupamachine(). Merged with default signOut. */
39
+ actions?: A;
40
+ children: React.ReactNode;
41
+ }
42
+ export type UseSupamachineReturn<C, D extends CustomStateConstraint | void = void, A extends Record<string, (...args: any[]) => any> = Record<string, (...args: any[]) => any>> = {
43
+ state: AppState<C, D>;
44
+ updateContext: (updater: (current: C) => C | Promise<C>) => Promise<void>;
45
+ beginAuth: () => void;
46
+ cancelAuth: () => void;
47
+ actions: SupamachineActions<A>;
48
+ };
49
+ export {};
@@ -0,0 +1,2 @@
1
+ // API-facing types for Supamachine
2
+ export {};
@@ -0,0 +1,5 @@
1
+ export { SupamachineProvider, useSupamachine, } from "./react/SupamachineProvider";
2
+ export type { SupamachineOptions } from "./react/SupamachineProvider";
3
+ export { AuthStateStatus } from "./core/constants";
4
+ export { LogLevel } from "./core/logger";
5
+ export type { AppState, DefaultActions, MapStateSnapshot, SupamachineActions, SupamachineProviderProps, UseSupamachineReturn, } from "./core/types";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { SupamachineProvider, useSupamachine, } from "./react/SupamachineProvider";
2
+ export { AuthStateStatus } from "./core/constants";
3
+ export { LogLevel } from "./core/logger";
@@ -0,0 +1,32 @@
1
+ import type { SupabaseClient } from "@supabase/supabase-js";
2
+ import type { SupamachineProviderProps, SupamachineActions } from "../core/types";
3
+ export type SupamachineOptions = {
4
+ getSessionTimeoutMs?: number;
5
+ loadContextTimeoutMs?: number;
6
+ initializeAppTimeoutMs?: number;
7
+ authenticatingTimeoutMs?: number;
8
+ /** 'none' | 'error' | 'warn' | 'info' | 'debug'. Default: 'warn'. Uses [Supamachine][subsystem] format. */
9
+ logLevel?: "none" | "error" | "warn" | "info" | "debug";
10
+ };
11
+ export interface SupamachineProviderPropsWithSupabase<C, D extends {
12
+ status: string;
13
+ } | void = void, A extends Record<string, (...args: any[]) => any> = Record<string, (...args: any[]) => any>> extends SupamachineProviderProps<C, D, A> {
14
+ supabase: SupabaseClient;
15
+ }
16
+ export interface SupamachineContextValue<C, D extends {
17
+ status: string;
18
+ } | void = void, A extends Record<string, (...args: any[]) => any> = Record<string, (...args: any[]) => any>> {
19
+ state: import("../core/types").AppState<C, D>;
20
+ updateContext: (updater: (current: C) => C | Promise<C>) => Promise<void>;
21
+ beginAuth: () => void;
22
+ cancelAuth: () => void;
23
+ actions: SupamachineActions<A>;
24
+ }
25
+ export declare function SupamachineProvider<C, D extends {
26
+ status: string;
27
+ } | void = void, A extends Record<string, (...args: any[]) => any> = Record<string, (...args: any[]) => any>>({ supabase, loadContext, mapState, initializeApp, actions: userActions, children, options: opts, }: SupamachineProviderPropsWithSupabase<C, D, A> & {
28
+ options?: SupamachineOptions;
29
+ }): import("react/jsx-runtime").JSX.Element | null;
30
+ export declare function useSupamachine<C, D extends {
31
+ status: string;
32
+ } | void = void, A extends Record<string, (...args: any[]) => any> = Record<string, (...args: any[]) => any>>(): SupamachineContextValue<C, D, A>;
@@ -0,0 +1,78 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
3
+ import { SupamachineCore } from "../core/runtime";
4
+ import { attachSupabase } from "../supabase/adapter";
5
+ import { AuthEventType } from "../core/constants";
6
+ import { parseLogLevel, LogLevel } from "../core/logger";
7
+ const defaultSignOut = (supabase) => () => {
8
+ void supabase.auth.signOut();
9
+ };
10
+ const SupamachineContext = createContext(null);
11
+ export function SupamachineProvider({ supabase, loadContext, mapState, initializeApp, actions: userActions, children, options: opts = {}, }) {
12
+ var _a;
13
+ const coreRef = useRef(null);
14
+ const unsubAdapterRef = useRef(null);
15
+ const [contextValue, setContextValue] = useState(null);
16
+ const actions = useMemo(() => ({
17
+ signOut: defaultSignOut(supabase),
18
+ ...userActions,
19
+ }), [supabase, userActions]);
20
+ const logLevel = typeof opts.logLevel === "string" ? parseLogLevel(opts.logLevel) : (_a = opts.logLevel) !== null && _a !== void 0 ? _a : LogLevel.WARN;
21
+ if (!coreRef.current) {
22
+ coreRef.current = new SupamachineCore({
23
+ loadContext,
24
+ mapState,
25
+ initializeApp,
26
+ loadContextTimeoutMs: opts.loadContextTimeoutMs,
27
+ initializeAppTimeoutMs: opts.initializeAppTimeoutMs,
28
+ authenticatingTimeoutMs: opts.authenticatingTimeoutMs,
29
+ logLevel,
30
+ });
31
+ unsubAdapterRef.current = attachSupabase(coreRef.current, supabase, {
32
+ getSessionTimeoutMs: opts.getSessionTimeoutMs,
33
+ logLevel,
34
+ });
35
+ coreRef.current.dispatch({ type: AuthEventType.START });
36
+ }
37
+ // Stable references — coreRef.current is set once and never changes
38
+ const updateContext = useCallback((updater) => coreRef.current.updateContext(updater), []);
39
+ const beginAuth = useCallback(() => coreRef.current.beginAuth(), []);
40
+ const cancelAuth = useCallback(() => coreRef.current.cancelAuth(), []);
41
+ if (!contextValue) {
42
+ setContextValue({
43
+ state: coreRef.current.getAppState(),
44
+ updateContext,
45
+ beginAuth,
46
+ cancelAuth,
47
+ actions,
48
+ });
49
+ }
50
+ useEffect(() => {
51
+ const core = coreRef.current;
52
+ const unsubscribe = core.subscribe(() => {
53
+ setContextValue({
54
+ state: core.getAppState(),
55
+ updateContext,
56
+ beginAuth,
57
+ cancelAuth,
58
+ actions,
59
+ });
60
+ });
61
+ return () => {
62
+ var _a;
63
+ unsubscribe();
64
+ (_a = unsubAdapterRef.current) === null || _a === void 0 ? void 0 : _a.call(unsubAdapterRef);
65
+ };
66
+ }, [actions, updateContext, beginAuth, cancelAuth]);
67
+ if (!contextValue) {
68
+ return null;
69
+ }
70
+ return (_jsx(SupamachineContext.Provider, { value: contextValue, children: children }));
71
+ }
72
+ export function useSupamachine() {
73
+ const ctx = useContext(SupamachineContext);
74
+ if (!ctx) {
75
+ throw new Error("useSupamachine must be used within SupamachineProvider");
76
+ }
77
+ return ctx;
78
+ }
@@ -0,0 +1 @@
1
+ export { useSupamachine } from './SupamachineProvider';
@@ -0,0 +1 @@
1
+ export { useSupamachine } from './SupamachineProvider';
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Core machine states. Use these when checking state or in mapState.
3
+ * Distinct from AuthEventType (event types that drive transitions).
4
+ */
5
+ export declare const AuthStateStatus: {
6
+ readonly START: "START";
7
+ readonly CHECKING: "CHECKING";
8
+ readonly SIGNED_OUT: "SIGNED_OUT";
9
+ readonly CONTEXT_LOADING: "CONTEXT_LOADING";
10
+ readonly INITIALIZING: "INITIALIZING";
11
+ readonly AUTH_READY: "AUTH_READY";
12
+ readonly ERROR_CHECKING: "ERROR_CHECKING";
13
+ readonly ERROR_CONTEXT: "ERROR_CONTEXT";
14
+ readonly ERROR_INITIALIZING: "ERROR_INITIALIZING";
15
+ };
16
+ export type AuthStateStatus = (typeof AuthStateStatus)[keyof typeof AuthStateStatus];
17
+ /**
18
+ * Internal event types that drive the state machine.
19
+ * Mapped from Supabase auth events by the adapter.
20
+ */
21
+ export declare const AuthEventType: {
22
+ readonly START: "START";
23
+ readonly AUTH_RESOLVED: "AUTH_RESOLVED";
24
+ readonly AUTH_CHANGED: "AUTH_CHANGED";
25
+ readonly CONTEXT_RESOLVED: "CONTEXT_RESOLVED";
26
+ readonly INITIALIZED: "INITIALIZED";
27
+ readonly ERROR_CHECKING: "ERROR_CHECKING";
28
+ readonly ERROR_CONTEXT: "ERROR_CONTEXT";
29
+ readonly ERROR_INITIALIZING: "ERROR_INITIALIZING";
30
+ };
31
+ export type AuthEventType = (typeof AuthEventType)[keyof typeof AuthEventType];
@@ -0,0 +1,25 @@
1
+ import type { Session } from "@supabase/supabase-js";
2
+ import { AuthEventType } from "./constants";
3
+ export type AuthEvent<C> = {
4
+ type: typeof AuthEventType.START;
5
+ } | {
6
+ type: typeof AuthEventType.AUTH_RESOLVED;
7
+ session: Session | null;
8
+ } | {
9
+ type: typeof AuthEventType.AUTH_CHANGED;
10
+ session: Session | null;
11
+ } | {
12
+ type: "CONTEXT_RESOLVED";
13
+ context: C;
14
+ } | {
15
+ type: "INITIALIZED";
16
+ } | {
17
+ type: "ERROR_CHECKING";
18
+ error: Error;
19
+ } | {
20
+ type: "ERROR_CONTEXT";
21
+ error: Error;
22
+ } | {
23
+ type: "ERROR_INITIALIZING";
24
+ error: Error;
25
+ };
@@ -0,0 +1,15 @@
1
+ export declare const LogLevel: {
2
+ readonly NONE: 0;
3
+ readonly ERROR: 1;
4
+ readonly WARN: 2;
5
+ readonly INFO: 3;
6
+ readonly DEBUG: 4;
7
+ };
8
+ export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];
9
+ export declare function createLogger(subsystem: string, level: LogLevel): {
10
+ error: (message: string, ...args: unknown[]) => void;
11
+ warn: (message: string, ...args: unknown[]) => void;
12
+ info: (message: string, ...args: unknown[]) => void;
13
+ debug: (message: string, ...args: unknown[]) => void;
14
+ };
15
+ export declare function parseLogLevel(value: string | undefined): LogLevel;
@@ -0,0 +1,5 @@
1
+ import type { CoreState } from "./states";
2
+ import type { AuthEvent } from "./events";
3
+ import { type LogLevel } from "./logger";
4
+ export declare function setReducerLogLevel(level: LogLevel): void;
5
+ export declare function reducer<C>(state: CoreState<C>, event: AuthEvent<C>): CoreState<C>;
@@ -0,0 +1,39 @@
1
+ import type { Session } from "@supabase/supabase-js";
2
+ import type { CoreState } from "./states";
3
+ import type { AuthEvent } from "./events";
4
+ import { AuthStateStatus } from "./constants";
5
+ import type { AppState } from "./types";
6
+ import { type LogLevel } from "./logger";
7
+ export interface RuntimeOptions<C, D> {
8
+ loadContext?: (session: Session) => Promise<C>;
9
+ mapState?: (snapshot: Extract<CoreState<C>, {
10
+ status: typeof AuthStateStatus.AUTH_READY;
11
+ }>) => D;
12
+ initializeApp?: (snapshot: {
13
+ session: Session;
14
+ context: C;
15
+ updateContext: (updater: (current: C) => C | Promise<C>) => Promise<void>;
16
+ }) => void | Promise<void>;
17
+ loadContextTimeoutMs?: number;
18
+ initializeAppTimeoutMs?: number;
19
+ logLevel?: LogLevel;
20
+ }
21
+ export declare class SupamachineCore<C, D> {
22
+ private readonly options;
23
+ private state;
24
+ private sessionForLoading;
25
+ private listeners;
26
+ private loadContextTimeoutMs;
27
+ private initializeAppTimeoutMs;
28
+ private log;
29
+ constructor(options: RuntimeOptions<C, D>);
30
+ setLogLevel(level: LogLevel): void;
31
+ updateContext(updater: (current: C) => C | Promise<C>): Promise<void>;
32
+ dispatch(event: AuthEvent<C>): void;
33
+ private loadContextWithTimeout;
34
+ private initializeAppWithTimeout;
35
+ subscribe(fn: (coreState: CoreState<C>, appState: AppState<C, D>) => void): () => boolean;
36
+ getSnapshot(): CoreState<C>;
37
+ getAppState(): AppState<C, D>;
38
+ private emit;
39
+ }
@@ -0,0 +1,32 @@
1
+ import type { Session } from "@supabase/supabase-js";
2
+ import { AuthStateStatus } from "./constants";
3
+ export type CoreState<C> = {
4
+ status: typeof AuthStateStatus.START;
5
+ } | {
6
+ status: typeof AuthStateStatus.CHECKING;
7
+ } | {
8
+ status: typeof AuthStateStatus.ERROR_CHECKING;
9
+ error: Error;
10
+ } | {
11
+ status: typeof AuthStateStatus.SIGNED_OUT;
12
+ } | {
13
+ status: typeof AuthStateStatus.CONTEXT_LOADING;
14
+ session: Session;
15
+ } | {
16
+ status: typeof AuthStateStatus.ERROR_CONTEXT;
17
+ error: Error;
18
+ session: Session;
19
+ } | {
20
+ status: typeof AuthStateStatus.INITIALIZING;
21
+ session: Session;
22
+ context: C;
23
+ } | {
24
+ status: typeof AuthStateStatus.ERROR_INITIALIZING;
25
+ error: Error;
26
+ session: Session;
27
+ context: C;
28
+ } | {
29
+ status: typeof AuthStateStatus.AUTH_READY;
30
+ session: Session;
31
+ context: C | null;
32
+ };
@@ -0,0 +1,33 @@
1
+ import type { Session } from "@supabase/supabase-js";
2
+ import type { CoreState } from "./states";
3
+ import { AuthStateStatus } from "./constants";
4
+ type DisallowedStatuses = typeof AuthStateStatus.START | typeof AuthStateStatus.CHECKING | typeof AuthStateStatus.ERROR_CHECKING | typeof AuthStateStatus.SIGNED_OUT | typeof AuthStateStatus.CONTEXT_LOADING | typeof AuthStateStatus.ERROR_CONTEXT | typeof AuthStateStatus.INITIALIZING | typeof AuthStateStatus.ERROR_INITIALIZING | typeof AuthStateStatus.AUTH_READY;
5
+ type CustomStateConstraint = {
6
+ status: Exclude<string, DisallowedStatuses>;
7
+ };
8
+ type WithSessionContext<C, T> = T extends {
9
+ status: string;
10
+ } ? T & {
11
+ session: Session;
12
+ context: C | null;
13
+ } : never;
14
+ /** When D is void (default), returns CoreState. When D is custom states, returns core states | (D with session+context added) */
15
+ export type AppState<C, D = void> = [D] extends [void] ? CoreState<C> : Exclude<CoreState<C>, {
16
+ status: typeof AuthStateStatus.AUTH_READY;
17
+ }> | WithSessionContext<C, D>;
18
+ export interface SupamachineProviderProps<C, D extends CustomStateConstraint | void = void> {
19
+ loadContext?: (session: Session) => Promise<C>;
20
+ initializeApp?: (snapshot: {
21
+ session: Session;
22
+ context: C;
23
+ }) => void | Promise<void>;
24
+ mapState?: [D] extends [void] ? never : (snapshot: Extract<CoreState<C>, {
25
+ status: typeof AuthStateStatus.AUTH_READY;
26
+ }>) => D;
27
+ children: React.ReactNode;
28
+ }
29
+ export type UseSupamachineReturn<C, D extends CustomStateConstraint | void = void> = {
30
+ state: AppState<C, D>;
31
+ updateContext: (updater: (current: C) => C | Promise<C>) => Promise<void>;
32
+ };
33
+ export {};
@@ -0,0 +1,5 @@
1
+ export { SupamachineProvider, useSupamachine, } from "./react/SupamachineProvider";
2
+ export type { SupamachineOptions } from "./react/SupamachineProvider";
3
+ export { AuthStateStatus } from "./core/constants";
4
+ export { LogLevel } from "./core/logger";
5
+ export type { AppState, SupamachineProviderProps, UseSupamachineReturn, } from "./core/types";
@@ -0,0 +1,28 @@
1
+ import type { SupabaseClient } from "@supabase/supabase-js";
2
+ import type { SupamachineProviderProps } from "../core/types";
3
+ export type SupamachineOptions = {
4
+ getSessionTimeoutMs?: number;
5
+ loadContextTimeoutMs?: number;
6
+ initializeAppTimeoutMs?: number;
7
+ /** 'none' | 'error' | 'warn' | 'info' | 'debug'. Default: 'warn'. Uses [Supamachine][subsystem] format. */
8
+ logLevel?: "none" | "error" | "warn" | "info" | "debug";
9
+ };
10
+ export interface SupamachineProviderPropsWithSupabase<C, D extends {
11
+ status: string;
12
+ } | void = void> extends SupamachineProviderProps<C, D> {
13
+ supabase: SupabaseClient;
14
+ }
15
+ export interface SupamachineContextValue<C, D extends {
16
+ status: string;
17
+ } | void = void> {
18
+ state: import("../core/types").AppState<C, D>;
19
+ updateContext: (updater: (current: C) => C | Promise<C>) => Promise<void>;
20
+ }
21
+ export declare function SupamachineProvider<C, D extends {
22
+ status: string;
23
+ } | void = void>({ supabase, loadContext, mapState, initializeApp, children, options: opts, }: SupamachineProviderPropsWithSupabase<C, D> & {
24
+ options?: SupamachineOptions;
25
+ }): import("react/jsx-runtime").JSX.Element | null;
26
+ export declare function useSupamachine<C, D extends {
27
+ status: string;
28
+ } | void = void>(): SupamachineContextValue<C, D>;
@@ -0,0 +1 @@
1
+ export { useSupamachine } from './SupamachineProvider';
@@ -0,0 +1,7 @@
1
+ import type { SupamachineCore } from "../core/runtime";
2
+ import type { SupabaseClient } from "@supabase/supabase-js";
3
+ import { type LogLevel } from "../core/logger";
4
+ export declare function attachSupabase<C, D>(core: SupamachineCore<C, D>, supabase: SupabaseClient, options?: {
5
+ getSessionTimeoutMs?: number;
6
+ logLevel?: LogLevel;
7
+ }): () => void;
@@ -0,0 +1,7 @@
1
+ import type { SupamachineCore } from "../core/runtime";
2
+ import type { SupabaseClient } from "@supabase/supabase-js";
3
+ import { type LogLevel } from "../core/logger";
4
+ export declare function attachSupabase<C, D>(core: SupamachineCore<C, D>, supabase: SupabaseClient, options?: {
5
+ getSessionTimeoutMs?: number;
6
+ logLevel?: LogLevel;
7
+ }): () => void;