@sincpro/mobile 0.3.1 → 0.4.1
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/README.md +234 -2
- package/dist/entrypoints/db/migrations.d.ts +3 -1
- package/dist/entrypoints/db/migrations.js +65 -0
- package/dist/entrypoints/queue/QueueProcessor.js +1 -25
- package/dist/entrypoints/queue/subscriber_filter.d.ts +9 -0
- package/dist/entrypoints/queue/subscriber_filter.js +25 -0
- package/dist/framework/base_module.d.ts +1 -0
- package/dist/framework/base_module.js +6 -1
- package/dist/framework/createApp.d.ts +6 -0
- package/dist/framework/createApp.js +5 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/infrastructure/context_manager/context.d.ts +16 -0
- package/dist/infrastructure/context_manager/context.js +37 -0
- package/dist/infrastructure/context_manager/context_api.d.ts +28 -0
- package/dist/infrastructure/context_manager/context_api.js +55 -0
- package/dist/infrastructure/context_manager/context_key.d.ts +15 -0
- package/dist/infrastructure/context_manager/context_key.js +3 -0
- package/dist/infrastructure/context_manager/context_manager_facade.d.ts +107 -0
- package/dist/infrastructure/context_manager/context_manager_facade.js +113 -0
- package/dist/infrastructure/context_manager/decorators/propagate_context.d.ts +24 -0
- package/dist/infrastructure/context_manager/decorators/propagate_context.js +52 -0
- package/dist/infrastructure/context_manager/decorators/with_context.d.ts +34 -0
- package/dist/infrastructure/context_manager/decorators/with_context.js +44 -0
- package/dist/infrastructure/context_manager/index.d.ts +11 -0
- package/dist/infrastructure/context_manager/index.js +12 -0
- package/dist/infrastructure/context_manager/managed_context.d.ts +87 -0
- package/dist/infrastructure/context_manager/managed_context.js +144 -0
- package/dist/infrastructure/context_manager/stack_context_manager.d.ts +39 -0
- package/dist/infrastructure/context_manager/stack_context_manager.js +56 -0
- package/dist/infrastructure/database/connector.js +10 -1
- package/dist/infrastructure/interceptor/index.d.ts +4 -0
- package/dist/infrastructure/interceptor/index.js +2 -0
- package/dist/infrastructure/interceptor/intercept.d.ts +57 -0
- package/dist/infrastructure/interceptor/intercept.js +112 -0
- package/dist/infrastructure/interceptor/logging_hooks.d.ts +22 -0
- package/dist/infrastructure/interceptor/logging_hooks.js +27 -0
- package/dist/infrastructure/logger.d.ts +11 -16
- package/dist/infrastructure/logger.js +61 -18
- package/dist/infrastructure/telemetry/buffer_registry.d.ts +15 -0
- package/dist/infrastructure/telemetry/buffer_registry.js +26 -0
- package/dist/infrastructure/telemetry/connectivity_state.d.ts +20 -0
- package/dist/infrastructure/telemetry/connectivity_state.js +28 -0
- package/dist/infrastructure/telemetry/context_keys.d.ts +7 -0
- package/dist/infrastructure/telemetry/context_keys.js +7 -0
- package/dist/infrastructure/telemetry/fetch_with_timeout.d.ts +8 -0
- package/dist/infrastructure/telemetry/fetch_with_timeout.js +17 -0
- package/dist/infrastructure/telemetry/flush_telemetry.d.ts +40 -0
- package/dist/infrastructure/telemetry/flush_telemetry.js +72 -0
- package/dist/infrastructure/telemetry/index.d.ts +17 -0
- package/dist/infrastructure/telemetry/index.js +17 -0
- package/dist/infrastructure/telemetry/init_telemetry.d.ts +21 -0
- package/dist/infrastructure/telemetry/init_telemetry.js +85 -0
- package/dist/infrastructure/telemetry/logging/flush_job.d.ts +44 -0
- package/dist/infrastructure/telemetry/logging/flush_job.js +69 -0
- package/dist/infrastructure/telemetry/logging/log_queue_repository.d.ts +76 -0
- package/dist/infrastructure/telemetry/logging/log_queue_repository.js +147 -0
- package/dist/infrastructure/telemetry/logging/loki_client.d.ts +32 -0
- package/dist/infrastructure/telemetry/logging/loki_client.js +118 -0
- package/dist/infrastructure/telemetry/logging/loki_registry.d.ts +12 -0
- package/dist/infrastructure/telemetry/logging/loki_registry.js +17 -0
- package/dist/infrastructure/telemetry/telemetry_flush_worker.d.ts +35 -0
- package/dist/infrastructure/telemetry/telemetry_flush_worker.js +65 -0
- package/dist/infrastructure/telemetry/telemetry_signal.d.ts +18 -0
- package/dist/infrastructure/telemetry/telemetry_signal.js +33 -0
- package/dist/infrastructure/telemetry/tracing/active_span.d.ts +22 -0
- package/dist/infrastructure/telemetry/tracing/active_span.js +40 -0
- package/dist/infrastructure/telemetry/tracing/otlp_client.d.ts +13 -0
- package/dist/infrastructure/telemetry/tracing/otlp_client.js +34 -0
- package/dist/infrastructure/telemetry/tracing/otlp_serializer.d.ts +67 -0
- package/dist/infrastructure/telemetry/tracing/otlp_serializer.js +101 -0
- package/dist/infrastructure/telemetry/tracing/span_flush_job.d.ts +25 -0
- package/dist/infrastructure/telemetry/tracing/span_flush_job.js +42 -0
- package/dist/infrastructure/telemetry/tracing/span_queue_repository.d.ts +82 -0
- package/dist/infrastructure/telemetry/tracing/span_queue_repository.js +134 -0
- package/dist/infrastructure/telemetry/tracing/span_sampler.d.ts +40 -0
- package/dist/infrastructure/telemetry/tracing/span_sampler.js +75 -0
- package/dist/infrastructure/telemetry/tracing/span_serializer.d.ts +48 -0
- package/dist/infrastructure/telemetry/tracing/span_serializer.js +36 -0
- package/dist/infrastructure/telemetry/tracing/sqlite_span_exporter.d.ts +26 -0
- package/dist/infrastructure/telemetry/tracing/sqlite_span_exporter.js +47 -0
- package/dist/infrastructure/telemetry/tracing/tracer.d.ts +22 -0
- package/dist/infrastructure/telemetry/tracing/tracer.js +45 -0
- package/dist/infrastructure/telemetry/tracing/tracing_hooks.d.ts +10 -0
- package/dist/infrastructure/telemetry/tracing/tracing_hooks.js +59 -0
- package/dist/infrastructure/telemetry/tracing_facade.d.ts +50 -0
- package/dist/infrastructure/telemetry/tracing_facade.js +139 -0
- package/dist/infrastructure/telemetry/types.d.ts +120 -0
- package/dist/infrastructure/telemetry/types.js +1 -0
- package/dist/ui/components/molecules/TimeZoneSelectorModal.js +7 -1
- package/package.json +17 -11
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opaque typed key for storing values in a Context.
|
|
3
|
+
*
|
|
4
|
+
* Two calls to createContextKey with the same description produce different
|
|
5
|
+
* keys — collision between modules is impossible even if they share names.
|
|
6
|
+
*
|
|
7
|
+
* _type_marker is a phantom field: it only exists in the TypeScript type
|
|
8
|
+
* system to carry T through inference. It is never assigned at runtime.
|
|
9
|
+
*/
|
|
10
|
+
export type ContextKey<T> = {
|
|
11
|
+
readonly description: string;
|
|
12
|
+
readonly _symbol: symbol;
|
|
13
|
+
readonly _type_marker: T;
|
|
14
|
+
};
|
|
15
|
+
export declare function createContextKey<T>(description: string): ContextKey<T>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Context } from "./context";
|
|
2
|
+
import type { ContextKey } from "./context_key";
|
|
3
|
+
import { createContextKey } from "./context_key";
|
|
4
|
+
import { PropagateContext } from "./decorators/propagate_context";
|
|
5
|
+
import { SetContext, WithContext } from "./decorators/with_context";
|
|
6
|
+
import { managed, ManagedContext } from "./managed_context";
|
|
7
|
+
export declare function run<T>(transform: (ctx: Context) => Context, fn: () => T | Promise<T>): T | Promise<T>;
|
|
8
|
+
export declare const ContextManager: {
|
|
9
|
+
/**
|
|
10
|
+
* Creates a typed key for storing values in the context.
|
|
11
|
+
* Each call produces a unique key — define it as a module-level constant.
|
|
12
|
+
* @example
|
|
13
|
+
* const TENANT_KEY = ContextManager.createKey<string>("tenant");
|
|
14
|
+
*/
|
|
15
|
+
readonly createKey: typeof createContextKey;
|
|
16
|
+
/**
|
|
17
|
+
* Reads the value for a key from the active context.
|
|
18
|
+
* Returns undefined if the key was not set in the current stack.
|
|
19
|
+
*/
|
|
20
|
+
readonly get: <T>(key: ContextKey<T>) => T | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the full active Context (immutable snapshot).
|
|
23
|
+
*/
|
|
24
|
+
readonly active: () => Context;
|
|
25
|
+
/**
|
|
26
|
+
* Runs fn within a transformed context.
|
|
27
|
+
* The original context is restored automatically on exit, even on error.
|
|
28
|
+
* @example
|
|
29
|
+
* await ContextManager.run(
|
|
30
|
+
* (ctx) => ctx.set(TENANT_KEY, "acme").set(USER_KEY, user),
|
|
31
|
+
* async () => processOrder(order),
|
|
32
|
+
* );
|
|
33
|
+
*/
|
|
34
|
+
readonly run: typeof run;
|
|
35
|
+
/**
|
|
36
|
+
* Class decorator. Captures the active context at each method call site and
|
|
37
|
+
* ensures the method body runs within it. Useful for callbacks, event handlers,
|
|
38
|
+
* or any pattern where the method is invoked outside the original call stack.
|
|
39
|
+
* @example
|
|
40
|
+
* \@ContextManager.Propagate
|
|
41
|
+
* class BleNotificationHandler { ... }
|
|
42
|
+
*/
|
|
43
|
+
readonly Propagate: typeof PropagateContext;
|
|
44
|
+
/**
|
|
45
|
+
* Method decorator. Enriches the active context before the method runs.
|
|
46
|
+
* Receives the current context and the instance (`self`), returns the new context.
|
|
47
|
+
* @example
|
|
48
|
+
* class BleOrchestrator {
|
|
49
|
+
* \@ContextManager.Enrich((ctx, self) =>
|
|
50
|
+
* ctx.set(DEVICE_KEY, (self as BleOrchestrator).activeDevice)
|
|
51
|
+
* )
|
|
52
|
+
* async startSession() { ... }
|
|
53
|
+
* }
|
|
54
|
+
*/
|
|
55
|
+
readonly Enrich: typeof WithContext;
|
|
56
|
+
/**
|
|
57
|
+
* Shorthand for setting a single key before a method runs.
|
|
58
|
+
* @example
|
|
59
|
+
* \@ContextManager.Set(TENANT_KEY, "clinic-001")
|
|
60
|
+
* async handleRequest() { ... }
|
|
61
|
+
*/
|
|
62
|
+
readonly Set: typeof SetContext;
|
|
63
|
+
/**
|
|
64
|
+
* Generator flavor — define acquire and release in one block, `yield` is the hand-off.
|
|
65
|
+
*
|
|
66
|
+
* Code before `yield` runs on enter; code after (or inside `catch`) runs on exit.
|
|
67
|
+
* Pass an optional `key` to also store the value under that key so nested
|
|
68
|
+
* code can read it via `ContextManager.get(key)`. Without a key, use `.get()`.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // Auto key
|
|
72
|
+
* const dbSession = ContextManager.managed(async function* () {
|
|
73
|
+
* const session = await db.openSession();
|
|
74
|
+
* try { yield session; await session.commit(); }
|
|
75
|
+
* catch (e) { await session.rollback(); throw e; }
|
|
76
|
+
* });
|
|
77
|
+
* await dbSession.use(async () => { const s = dbSession.get()!; });
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* // Shared key
|
|
81
|
+
* const SESSION_KEY = ContextManager.createKey<DbSession>("db.session");
|
|
82
|
+
* const dbSession = ContextManager.managed(async function* () {
|
|
83
|
+
* yield await db.openSession();
|
|
84
|
+
* }, SESSION_KEY);
|
|
85
|
+
* await dbSession.use(async () => { const s = ContextManager.get(SESSION_KEY)!; });
|
|
86
|
+
*/
|
|
87
|
+
readonly managed: typeof managed;
|
|
88
|
+
/**
|
|
89
|
+
* Class flavor — extend and implement `open()` and `close()`.
|
|
90
|
+
*
|
|
91
|
+
* Familiar OOP shape; gives explicit types and IDE completion on each hook.
|
|
92
|
+
* The key is managed internally — read the value via `.get()`.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* class DbSessionContext extends ContextManager.ManagedContext<DbSession> {
|
|
96
|
+
* open() { return db.openSession(); }
|
|
97
|
+
* close(session, error?) { return error ? session.rollback() : session.commit(); }
|
|
98
|
+
* }
|
|
99
|
+
*
|
|
100
|
+
* const dbSession = new DbSessionContext();
|
|
101
|
+
* await dbSession.use(async () => {
|
|
102
|
+
* const session = dbSession.get()!;
|
|
103
|
+
* await session.insert(order);
|
|
104
|
+
* });
|
|
105
|
+
*/
|
|
106
|
+
readonly ManagedContext: typeof ManagedContext;
|
|
107
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { getActiveContext, getContext, runWithContext } from "./context_api";
|
|
2
|
+
import { createContextKey } from "./context_key";
|
|
3
|
+
import { PropagateContext } from "./decorators/propagate_context";
|
|
4
|
+
import { SetContext, WithContext } from "./decorators/with_context";
|
|
5
|
+
import { managed, ManagedContext } from "./managed_context";
|
|
6
|
+
export function run(transform, fn) {
|
|
7
|
+
const ctx = transform(getActiveContext());
|
|
8
|
+
return runWithContext(ctx, fn);
|
|
9
|
+
}
|
|
10
|
+
export const ContextManager = {
|
|
11
|
+
/**
|
|
12
|
+
* Creates a typed key for storing values in the context.
|
|
13
|
+
* Each call produces a unique key — define it as a module-level constant.
|
|
14
|
+
* @example
|
|
15
|
+
* const TENANT_KEY = ContextManager.createKey<string>("tenant");
|
|
16
|
+
*/
|
|
17
|
+
createKey: createContextKey,
|
|
18
|
+
/**
|
|
19
|
+
* Reads the value for a key from the active context.
|
|
20
|
+
* Returns undefined if the key was not set in the current stack.
|
|
21
|
+
*/
|
|
22
|
+
get(key) {
|
|
23
|
+
return getContext(key);
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* Returns the full active Context (immutable snapshot).
|
|
27
|
+
*/
|
|
28
|
+
active() {
|
|
29
|
+
return getActiveContext();
|
|
30
|
+
},
|
|
31
|
+
/**
|
|
32
|
+
* Runs fn within a transformed context.
|
|
33
|
+
* The original context is restored automatically on exit, even on error.
|
|
34
|
+
* @example
|
|
35
|
+
* await ContextManager.run(
|
|
36
|
+
* (ctx) => ctx.set(TENANT_KEY, "acme").set(USER_KEY, user),
|
|
37
|
+
* async () => processOrder(order),
|
|
38
|
+
* );
|
|
39
|
+
*/
|
|
40
|
+
run,
|
|
41
|
+
/**
|
|
42
|
+
* Class decorator. Captures the active context at each method call site and
|
|
43
|
+
* ensures the method body runs within it. Useful for callbacks, event handlers,
|
|
44
|
+
* or any pattern where the method is invoked outside the original call stack.
|
|
45
|
+
* @example
|
|
46
|
+
* \@ContextManager.Propagate
|
|
47
|
+
* class BleNotificationHandler { ... }
|
|
48
|
+
*/
|
|
49
|
+
Propagate: PropagateContext,
|
|
50
|
+
/**
|
|
51
|
+
* Method decorator. Enriches the active context before the method runs.
|
|
52
|
+
* Receives the current context and the instance (`self`), returns the new context.
|
|
53
|
+
* @example
|
|
54
|
+
* class BleOrchestrator {
|
|
55
|
+
* \@ContextManager.Enrich((ctx, self) =>
|
|
56
|
+
* ctx.set(DEVICE_KEY, (self as BleOrchestrator).activeDevice)
|
|
57
|
+
* )
|
|
58
|
+
* async startSession() { ... }
|
|
59
|
+
* }
|
|
60
|
+
*/
|
|
61
|
+
Enrich: WithContext,
|
|
62
|
+
/**
|
|
63
|
+
* Shorthand for setting a single key before a method runs.
|
|
64
|
+
* @example
|
|
65
|
+
* \@ContextManager.Set(TENANT_KEY, "clinic-001")
|
|
66
|
+
* async handleRequest() { ... }
|
|
67
|
+
*/
|
|
68
|
+
Set: SetContext,
|
|
69
|
+
/**
|
|
70
|
+
* Generator flavor — define acquire and release in one block, `yield` is the hand-off.
|
|
71
|
+
*
|
|
72
|
+
* Code before `yield` runs on enter; code after (or inside `catch`) runs on exit.
|
|
73
|
+
* Pass an optional `key` to also store the value under that key so nested
|
|
74
|
+
* code can read it via `ContextManager.get(key)`. Without a key, use `.get()`.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // Auto key
|
|
78
|
+
* const dbSession = ContextManager.managed(async function* () {
|
|
79
|
+
* const session = await db.openSession();
|
|
80
|
+
* try { yield session; await session.commit(); }
|
|
81
|
+
* catch (e) { await session.rollback(); throw e; }
|
|
82
|
+
* });
|
|
83
|
+
* await dbSession.use(async () => { const s = dbSession.get()!; });
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Shared key
|
|
87
|
+
* const SESSION_KEY = ContextManager.createKey<DbSession>("db.session");
|
|
88
|
+
* const dbSession = ContextManager.managed(async function* () {
|
|
89
|
+
* yield await db.openSession();
|
|
90
|
+
* }, SESSION_KEY);
|
|
91
|
+
* await dbSession.use(async () => { const s = ContextManager.get(SESSION_KEY)!; });
|
|
92
|
+
*/
|
|
93
|
+
managed,
|
|
94
|
+
/**
|
|
95
|
+
* Class flavor — extend and implement `open()` and `close()`.
|
|
96
|
+
*
|
|
97
|
+
* Familiar OOP shape; gives explicit types and IDE completion on each hook.
|
|
98
|
+
* The key is managed internally — read the value via `.get()`.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* class DbSessionContext extends ContextManager.ManagedContext<DbSession> {
|
|
102
|
+
* open() { return db.openSession(); }
|
|
103
|
+
* close(session, error?) { return error ? session.rollback() : session.commit(); }
|
|
104
|
+
* }
|
|
105
|
+
*
|
|
106
|
+
* const dbSession = new DbSessionContext();
|
|
107
|
+
* await dbSession.use(async () => {
|
|
108
|
+
* const session = dbSession.get()!;
|
|
109
|
+
* await session.insert(order);
|
|
110
|
+
* });
|
|
111
|
+
*/
|
|
112
|
+
ManagedContext,
|
|
113
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type AnyClass = new (...args: never[]) => object;
|
|
2
|
+
/**
|
|
3
|
+
* Class decorator. Captures the active context at the moment each method is
|
|
4
|
+
* called and ensures the method body runs within that context.
|
|
5
|
+
*
|
|
6
|
+
* Useful for classes whose methods may be invoked from callbacks, event
|
|
7
|
+
* handlers, or fire-and-forget patterns where the caller's context would
|
|
8
|
+
* otherwise be lost by the time the method executes.
|
|
9
|
+
*
|
|
10
|
+
* For the common sequential-await pattern, context propagates automatically
|
|
11
|
+
* via the stack — @PropagateContext adds value mainly when methods are called
|
|
12
|
+
* outside of a runWithContext block or after a timer/event boundary.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* @PropagateContext
|
|
16
|
+
* class BleNotificationService {
|
|
17
|
+
* onHeartRateReading(value: number) {
|
|
18
|
+
* // context active when the listener was registered is visible here
|
|
19
|
+
* const device = getContext(DEVICE_KEY);
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*/
|
|
23
|
+
export declare function PropagateContext<T extends AnyClass>(target: T): T;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { getActiveContext, runWithContext } from "../context_api";
|
|
2
|
+
const PROPAGATED = Symbol("sincpro.context.propagated");
|
|
3
|
+
/**
|
|
4
|
+
* Class decorator. Captures the active context at the moment each method is
|
|
5
|
+
* called and ensures the method body runs within that context.
|
|
6
|
+
*
|
|
7
|
+
* Useful for classes whose methods may be invoked from callbacks, event
|
|
8
|
+
* handlers, or fire-and-forget patterns where the caller's context would
|
|
9
|
+
* otherwise be lost by the time the method executes.
|
|
10
|
+
*
|
|
11
|
+
* For the common sequential-await pattern, context propagates automatically
|
|
12
|
+
* via the stack — @PropagateContext adds value mainly when methods are called
|
|
13
|
+
* outside of a runWithContext block or after a timer/event boundary.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* @PropagateContext
|
|
17
|
+
* class BleNotificationService {
|
|
18
|
+
* onHeartRateReading(value: number) {
|
|
19
|
+
* // context active when the listener was registered is visible here
|
|
20
|
+
* const device = getContext(DEVICE_KEY);
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
export function PropagateContext(target) {
|
|
25
|
+
if (target[PROPAGATED]) {
|
|
26
|
+
throw new Error(`PropagateContext: ${target.name} is already decorated`);
|
|
27
|
+
}
|
|
28
|
+
const prototype = target.prototype;
|
|
29
|
+
for (const key of Object.getOwnPropertyNames(prototype)) {
|
|
30
|
+
if (key === "constructor")
|
|
31
|
+
continue;
|
|
32
|
+
const descriptor = Object.getOwnPropertyDescriptor(prototype, key);
|
|
33
|
+
if (!descriptor || typeof descriptor.value !== "function")
|
|
34
|
+
continue;
|
|
35
|
+
const original = descriptor.value;
|
|
36
|
+
descriptor.value = function (...args) {
|
|
37
|
+
const ctx = getActiveContext();
|
|
38
|
+
const result = runWithContext(ctx, () => original.apply(this, args));
|
|
39
|
+
if (result instanceof Promise) {
|
|
40
|
+
// Context was captured at call time (above). The Promise itself
|
|
41
|
+
// resolves outside the runWithContext scope — that is expected for
|
|
42
|
+
// the stack-based manager. Sequential awaits inside the method body
|
|
43
|
+
// already saw the correct context when they were initiated.
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
Object.defineProperty(prototype, key, descriptor);
|
|
49
|
+
}
|
|
50
|
+
Object.defineProperty(target, PROPAGATED, { value: true, enumerable: false });
|
|
51
|
+
return target;
|
|
52
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Context } from "../context";
|
|
2
|
+
import type { ContextKey } from "../context_key";
|
|
3
|
+
/**
|
|
4
|
+
* Method decorator. Before calling the method, applies transform to the
|
|
5
|
+
* active context and runs the method within the resulting context.
|
|
6
|
+
*
|
|
7
|
+
* Useful when a method needs to enrich the context with values available
|
|
8
|
+
* on the instance (e.g. the current BLE device, the active session ID).
|
|
9
|
+
*
|
|
10
|
+
* transform receives the current active context and must return the new one.
|
|
11
|
+
* The instance (this) is passed as second argument so instance properties
|
|
12
|
+
* are accessible without closure hacks.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* class BleOrchestrator {
|
|
16
|
+
* private activeDevice: BleDevice;
|
|
17
|
+
*
|
|
18
|
+
* @WithContext((ctx, self) =>
|
|
19
|
+
* ctx.set(DEVICE_KEY, (self as BleOrchestrator).activeDevice)
|
|
20
|
+
* )
|
|
21
|
+
* async startSession() {
|
|
22
|
+
* // DEVICE_KEY is now in context for every nested call
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
export declare function WithContext(transform: (ctx: Context, self: unknown) => Context): MethodDecorator;
|
|
27
|
+
/**
|
|
28
|
+
* Shorthand for setting a single key before a method runs.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* @SetContext(TENANT_KEY, "clinic-001")
|
|
32
|
+
* async handleRequest() { ... }
|
|
33
|
+
*/
|
|
34
|
+
export declare function SetContext<T>(key: ContextKey<T>, value: T): MethodDecorator;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getActiveContext, runWithContext } from "../context_api";
|
|
2
|
+
/**
|
|
3
|
+
* Method decorator. Before calling the method, applies transform to the
|
|
4
|
+
* active context and runs the method within the resulting context.
|
|
5
|
+
*
|
|
6
|
+
* Useful when a method needs to enrich the context with values available
|
|
7
|
+
* on the instance (e.g. the current BLE device, the active session ID).
|
|
8
|
+
*
|
|
9
|
+
* transform receives the current active context and must return the new one.
|
|
10
|
+
* The instance (this) is passed as second argument so instance properties
|
|
11
|
+
* are accessible without closure hacks.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* class BleOrchestrator {
|
|
15
|
+
* private activeDevice: BleDevice;
|
|
16
|
+
*
|
|
17
|
+
* @WithContext((ctx, self) =>
|
|
18
|
+
* ctx.set(DEVICE_KEY, (self as BleOrchestrator).activeDevice)
|
|
19
|
+
* )
|
|
20
|
+
* async startSession() {
|
|
21
|
+
* // DEVICE_KEY is now in context for every nested call
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
export function WithContext(transform) {
|
|
26
|
+
return function (_target, _key, descriptor) {
|
|
27
|
+
const original = descriptor.value;
|
|
28
|
+
descriptor.value = function (...args) {
|
|
29
|
+
const ctx = transform(getActiveContext(), this);
|
|
30
|
+
return runWithContext(ctx, () => original.apply(this, args));
|
|
31
|
+
};
|
|
32
|
+
return descriptor;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Shorthand for setting a single key before a method runs.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* @SetContext(TENANT_KEY, "clinic-001")
|
|
40
|
+
* async handleRequest() { ... }
|
|
41
|
+
*/
|
|
42
|
+
export function SetContext(key, value) {
|
|
43
|
+
return WithContext((ctx) => ctx.set(key, value));
|
|
44
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { ContextManager } from "./context_manager_facade";
|
|
2
|
+
export { Context } from "./context";
|
|
3
|
+
export { getActiveContext, getContext, runWithContext } from "./context_api";
|
|
4
|
+
export type { ContextKey } from "./context_key";
|
|
5
|
+
export { createContextKey } from "./context_key";
|
|
6
|
+
export { PropagateContext } from "./decorators/propagate_context";
|
|
7
|
+
export { SetContext, WithContext } from "./decorators/with_context";
|
|
8
|
+
export type { ContextScope } from "./managed_context";
|
|
9
|
+
export { managed, ManagedContext } from "./managed_context";
|
|
10
|
+
export { _getContextManager, _resetContextManager } from "./context_api";
|
|
11
|
+
export { StackContextManager } from "./stack_context_manager";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Main entry point — public facade for framework consumers
|
|
2
|
+
export { ContextManager } from "./context_manager_facade";
|
|
3
|
+
// Public API — safe for framework consumers
|
|
4
|
+
export { Context } from "./context";
|
|
5
|
+
export { getActiveContext, getContext, runWithContext } from "./context_api";
|
|
6
|
+
export { createContextKey } from "./context_key";
|
|
7
|
+
export { PropagateContext } from "./decorators/propagate_context";
|
|
8
|
+
export { SetContext, WithContext } from "./decorators/with_context";
|
|
9
|
+
export { managed, ManagedContext } from "./managed_context";
|
|
10
|
+
// Internal API — for framework adapters and tests only, not for app consumers
|
|
11
|
+
export { _getContextManager, _resetContextManager } from "./context_api";
|
|
12
|
+
export { StackContextManager } from "./stack_context_manager";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ContextKey } from "./context_key";
|
|
2
|
+
/**
|
|
3
|
+
* Contract exposed by both managed context flavors.
|
|
4
|
+
*
|
|
5
|
+
* Acquire the resource, run a block with it in context, then release —
|
|
6
|
+
* all guaranteed even on error.
|
|
7
|
+
*/
|
|
8
|
+
export interface ContextScope<T> {
|
|
9
|
+
/**
|
|
10
|
+
* Reads the current value from the active context.
|
|
11
|
+
* Returns `undefined` when called outside an active `use()` block.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* await dbSession.use(async () => {
|
|
15
|
+
* const session = dbSession.get(); // defined here
|
|
16
|
+
* });
|
|
17
|
+
* dbSession.get(); // undefined — outside the block
|
|
18
|
+
*/
|
|
19
|
+
get(): T | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Acquires the resource, pushes it into context, calls `fn`, then releases.
|
|
22
|
+
* The resource is available via `.get()` (or the `fn` argument) throughout
|
|
23
|
+
* the call, including any nested async code on the same stack.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* await dbSession.use(async (session) => {
|
|
27
|
+
* await session.insert(order);
|
|
28
|
+
* });
|
|
29
|
+
*/
|
|
30
|
+
use<R>(fn: (value: T) => R | Promise<R>): Promise<R>;
|
|
31
|
+
/**
|
|
32
|
+
* Method decorator. Wraps the decorated method in a `use()` call.
|
|
33
|
+
* The resource is acquired before the method runs and released after.
|
|
34
|
+
* Read the value inside the method via `.get()`.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* class OrderRepository {
|
|
38
|
+
* \@dbSession.inject
|
|
39
|
+
* async save(order: Order) {
|
|
40
|
+
* const session = dbSession.get()!;
|
|
41
|
+
* await session.insert(order);
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
*/
|
|
45
|
+
inject: MethodDecorator;
|
|
46
|
+
}
|
|
47
|
+
export declare function managed<T>(factory: () => AsyncGenerator<T, void, unknown>, key?: ContextKey<T>): ContextScope<T>;
|
|
48
|
+
/**
|
|
49
|
+
* Class flavor — extend and implement `open()` and `close()`.
|
|
50
|
+
*
|
|
51
|
+
* Gives explicit method signatures and IDE completion on each lifecycle hook.
|
|
52
|
+
* The key is managed internally; read the value via `.get()` anywhere in the
|
|
53
|
+
* active stack.
|
|
54
|
+
*
|
|
55
|
+
* Optionally declare a `readonly key` to share the value under a known key,
|
|
56
|
+
* making it readable via `ContextManager.get(key)` from nested code.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Auto key — read via instance.get()
|
|
60
|
+
* class DbSessionContext extends ContextManager.ManagedContext<DbSession> {
|
|
61
|
+
* open() { return db.openSession(); }
|
|
62
|
+
* close(session, error?) { return error ? session.rollback() : session.commit(); }
|
|
63
|
+
* }
|
|
64
|
+
* const dbSession = new DbSessionContext();
|
|
65
|
+
* await dbSession.use(async () => {
|
|
66
|
+
* const session = dbSession.get()!;
|
|
67
|
+
* });
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* // Shared key — readable via ContextManager.get(SESSION_KEY) from nested code
|
|
71
|
+
* const SESSION_KEY = ContextManager.createKey<DbSession>("db.session");
|
|
72
|
+
* class DbSessionContext extends ContextManager.ManagedContext<DbSession> {
|
|
73
|
+
* readonly key = SESSION_KEY;
|
|
74
|
+
* open() { return db.openSession(); }
|
|
75
|
+
* close(session, error?) { return error ? session.rollback() : session.commit(); }
|
|
76
|
+
* }
|
|
77
|
+
*/
|
|
78
|
+
export declare abstract class ManagedContext<T> implements ContextScope<T> {
|
|
79
|
+
readonly key?: ContextKey<T>;
|
|
80
|
+
private _scope;
|
|
81
|
+
private _getScope;
|
|
82
|
+
abstract open(): T | Promise<T>;
|
|
83
|
+
abstract close(value: T, error?: unknown): void | Promise<void>;
|
|
84
|
+
get(): T | undefined;
|
|
85
|
+
use<R>(fn: (value: T) => R | Promise<R>): Promise<R>;
|
|
86
|
+
get inject(): MethodDecorator;
|
|
87
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { _getContextManager, getContext } from "./context_api";
|
|
2
|
+
import { createContextKey } from "./context_key";
|
|
3
|
+
function buildScope(acquire, release, sharedKey) {
|
|
4
|
+
const key = sharedKey ?? createContextKey("sincpro.managed");
|
|
5
|
+
async function run(fn) {
|
|
6
|
+
const value = await acquire();
|
|
7
|
+
const manager = _getContextManager();
|
|
8
|
+
manager.push(manager.active().set(key, value));
|
|
9
|
+
let thrownError;
|
|
10
|
+
try {
|
|
11
|
+
return await fn(value);
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
thrownError = e;
|
|
15
|
+
throw e;
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
manager.pop();
|
|
19
|
+
await release(value, thrownError);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const inject = function (_target, _key, descriptor) {
|
|
23
|
+
const original = descriptor.value;
|
|
24
|
+
descriptor.value = function (...args) {
|
|
25
|
+
return run(() => original.apply(this, args));
|
|
26
|
+
};
|
|
27
|
+
return descriptor;
|
|
28
|
+
};
|
|
29
|
+
return { get: () => getContext(key), use: run, inject };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Generator flavor — define the full lifecycle in one block.
|
|
33
|
+
*
|
|
34
|
+
* Code before `yield` runs on enter; code after `yield` (or inside `catch`)
|
|
35
|
+
* runs on exit. The yielded value is the resource placed into context — it
|
|
36
|
+
* must be an object (a primitive cannot carry the lifecycle and throws).
|
|
37
|
+
*
|
|
38
|
+
* An optional `key` can be provided to also store the value under that key,
|
|
39
|
+
* making it readable via `ContextManager.get(key)` from anywhere on the stack.
|
|
40
|
+
* Without a key, use `.get()` on the scope itself.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Auto key — read via scope.get()
|
|
44
|
+
* const dbSession = ContextManager.managed(async function* () {
|
|
45
|
+
* const session = await db.openSession();
|
|
46
|
+
* try {
|
|
47
|
+
* yield session;
|
|
48
|
+
* await session.commit();
|
|
49
|
+
* } catch (e) {
|
|
50
|
+
* await session.rollback();
|
|
51
|
+
* throw e;
|
|
52
|
+
* }
|
|
53
|
+
* });
|
|
54
|
+
* await dbSession.use(async () => {
|
|
55
|
+
* const session = dbSession.get()!;
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Shared key — readable via ContextManager.get(SESSION_KEY) from nested code
|
|
60
|
+
* const SESSION_KEY = ContextManager.createKey<DbSession>("db.session");
|
|
61
|
+
* const dbSession = ContextManager.managed(async function* () {
|
|
62
|
+
* yield await db.openSession();
|
|
63
|
+
* }, SESSION_KEY);
|
|
64
|
+
* await dbSession.use(async () => {
|
|
65
|
+
* const session = ContextManager.get(SESSION_KEY)!;
|
|
66
|
+
* });
|
|
67
|
+
*/
|
|
68
|
+
const GEN_SYM = Symbol("sincpro.managed.gen");
|
|
69
|
+
export function managed(factory, key) {
|
|
70
|
+
return buildScope(async () => {
|
|
71
|
+
const gen = factory();
|
|
72
|
+
const { value, done } = await gen.next();
|
|
73
|
+
if (done)
|
|
74
|
+
throw new Error("managed: generator did not yield a value");
|
|
75
|
+
// The generator is stamped onto the yielded value so `release` can resume it.
|
|
76
|
+
// A primitive cannot carry the stamp, which would silently break the
|
|
77
|
+
// commit/rollback lifecycle — fail fast instead.
|
|
78
|
+
if (value === null || (typeof value !== "object" && typeof value !== "function")) {
|
|
79
|
+
await gen
|
|
80
|
+
.throw(new Error("managed: yielded value must be an object"))
|
|
81
|
+
.catch(() => { });
|
|
82
|
+
throw new Error("managed: generator must yield an object (got a primitive)");
|
|
83
|
+
}
|
|
84
|
+
value[GEN_SYM] = gen;
|
|
85
|
+
return value;
|
|
86
|
+
}, async (value, error) => {
|
|
87
|
+
const gen = value[GEN_SYM];
|
|
88
|
+
if (!gen)
|
|
89
|
+
return;
|
|
90
|
+
if (error !== undefined) {
|
|
91
|
+
await gen.throw(error).catch(() => { });
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
await gen.next();
|
|
95
|
+
}
|
|
96
|
+
}, key);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Class flavor — extend and implement `open()` and `close()`.
|
|
100
|
+
*
|
|
101
|
+
* Gives explicit method signatures and IDE completion on each lifecycle hook.
|
|
102
|
+
* The key is managed internally; read the value via `.get()` anywhere in the
|
|
103
|
+
* active stack.
|
|
104
|
+
*
|
|
105
|
+
* Optionally declare a `readonly key` to share the value under a known key,
|
|
106
|
+
* making it readable via `ContextManager.get(key)` from nested code.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* // Auto key — read via instance.get()
|
|
110
|
+
* class DbSessionContext extends ContextManager.ManagedContext<DbSession> {
|
|
111
|
+
* open() { return db.openSession(); }
|
|
112
|
+
* close(session, error?) { return error ? session.rollback() : session.commit(); }
|
|
113
|
+
* }
|
|
114
|
+
* const dbSession = new DbSessionContext();
|
|
115
|
+
* await dbSession.use(async () => {
|
|
116
|
+
* const session = dbSession.get()!;
|
|
117
|
+
* });
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* // Shared key — readable via ContextManager.get(SESSION_KEY) from nested code
|
|
121
|
+
* const SESSION_KEY = ContextManager.createKey<DbSession>("db.session");
|
|
122
|
+
* class DbSessionContext extends ContextManager.ManagedContext<DbSession> {
|
|
123
|
+
* readonly key = SESSION_KEY;
|
|
124
|
+
* open() { return db.openSession(); }
|
|
125
|
+
* close(session, error?) { return error ? session.rollback() : session.commit(); }
|
|
126
|
+
* }
|
|
127
|
+
*/
|
|
128
|
+
export class ManagedContext {
|
|
129
|
+
key;
|
|
130
|
+
_scope;
|
|
131
|
+
// Lazy — subclass key field is set before first call, not at parent field init time
|
|
132
|
+
_getScope() {
|
|
133
|
+
return (this._scope ??= buildScope(() => this.open(), (v, e) => this.close(v, e), this.key));
|
|
134
|
+
}
|
|
135
|
+
get() {
|
|
136
|
+
return this._getScope().get();
|
|
137
|
+
}
|
|
138
|
+
use(fn) {
|
|
139
|
+
return this._getScope().use(fn);
|
|
140
|
+
}
|
|
141
|
+
get inject() {
|
|
142
|
+
return this._getScope().inject;
|
|
143
|
+
}
|
|
144
|
+
}
|