@arcote.tech/arc 0.3.6 → 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.
Files changed (40) hide show
  1. package/dist/adapters/auth-adapter.d.ts +35 -20
  2. package/dist/adapters/command-wire.d.ts +4 -2
  3. package/dist/adapters/event-wire.d.ts +28 -3
  4. package/dist/adapters/index.d.ts +1 -0
  5. package/dist/adapters/query-wire.d.ts +6 -4
  6. package/dist/adapters/wire.d.ts +7 -11
  7. package/dist/context-element/aggregate/aggregate-base.d.ts +31 -0
  8. package/dist/context-element/aggregate/aggregate-builder.d.ts +139 -0
  9. package/dist/context-element/aggregate/aggregate-data.d.ts +114 -0
  10. package/dist/context-element/aggregate/aggregate-element.d.ts +69 -0
  11. package/dist/context-element/aggregate/index.d.ts +31 -0
  12. package/dist/context-element/aggregate/simple-aggregate.d.ts +94 -0
  13. package/dist/context-element/command/command-context.d.ts +3 -32
  14. package/dist/context-element/context-element.d.ts +7 -1
  15. package/dist/context-element/element-context.d.ts +39 -0
  16. package/dist/context-element/index.d.ts +2 -0
  17. package/dist/context-element/route/route.d.ts +8 -26
  18. package/dist/context-element/static-view/static-view.d.ts +2 -2
  19. package/dist/context-element/view/index.d.ts +1 -1
  20. package/dist/data-storage/store-state.abstract.d.ts +1 -0
  21. package/dist/elements/branded.d.ts +1 -0
  22. package/dist/elements/object.d.ts +2 -2
  23. package/dist/elements/optional.d.ts +1 -1
  24. package/dist/fragment/arc-fragment.d.ts +18 -0
  25. package/dist/fragment/index.d.ts +2 -0
  26. package/dist/index.d.ts +16 -0
  27. package/dist/index.js +1006 -458
  28. package/dist/model/context-accessor.d.ts +51 -0
  29. package/dist/model/index.d.ts +4 -0
  30. package/dist/model/live-query/live-query.d.ts +4 -26
  31. package/dist/model/model-adapters.d.ts +5 -0
  32. package/dist/model/model-like.d.ts +9 -0
  33. package/dist/model/model.d.ts +10 -1
  34. package/dist/model/mutation-executor/mutation-executor.d.ts +2 -2
  35. package/dist/model/scoped-model.d.ts +66 -0
  36. package/dist/model/scoped-wire-proxy.d.ts +23 -0
  37. package/dist/streaming/index.d.ts +0 -2
  38. package/dist/streaming/streaming-live-query.d.ts +10 -12
  39. package/dist/streaming/streaming-query-cache.d.ts +25 -4
  40. package/package.json +1 -1
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Context Accessor — builds typed accessors from context elements without Proxy.
3
+ *
4
+ * Shared between query and mutation: iterates context elements,
5
+ * calls their queryContext() or mutateContext(), and wraps each method
6
+ * with an onCall handler.
7
+ *
8
+ * Two use cases:
9
+ * - Descriptor builder: onCall returns a ContextDescriptor (serializable)
10
+ * - Direct execution: onCall executes the method (React hooks, server calls)
11
+ */
12
+ import type { ArcContextAny } from "../context/context";
13
+ import type { ModelAdapters } from "./model-adapters";
14
+ /**
15
+ * Serializable descriptor for a context method call.
16
+ * Works for both queries and mutations.
17
+ */
18
+ export interface ContextDescriptor {
19
+ element: string;
20
+ method: string;
21
+ args: any[];
22
+ }
23
+ /**
24
+ * Callback invoked when an accessor method is called.
25
+ * Receives the descriptor and a thunk to execute the original method.
26
+ */
27
+ export type OnAccessorCall<R> = (descriptor: ContextDescriptor, execute: () => any) => R;
28
+ /**
29
+ * Build a typed accessor object from context elements.
30
+ *
31
+ * Iterates all elements, calls `contextMethod` (e.g., "queryContext" or "mutateContext")
32
+ * on each, and wraps every returned method with the `onCall` handler.
33
+ *
34
+ * Result shape: `{ elementName: { methodName: (...args) => onCall(...) } }`
35
+ *
36
+ * @param context - The ArcContext containing elements
37
+ * @param adapters - ModelAdapters passed to queryContext/mutateContext
38
+ * @param contextMethod - "queryContext" or "mutateContext"
39
+ * @param onCall - Handler called when a method is invoked
40
+ */
41
+ export declare function buildContextAccessor<C extends ArcContextAny, R>(context: C, adapters: ModelAdapters, contextMethod: "queryContext" | "mutateContext", onCall: OnAccessorCall<R>): Record<string, Record<string, (...args: any[]) => R>>;
42
+ /**
43
+ * Execute a context descriptor against a model.
44
+ *
45
+ * @param descriptor - The serialized descriptor
46
+ * @param context - ArcContext containing elements
47
+ * @param adapters - ModelAdapters for queryContext/mutateContext
48
+ * @param contextMethod - "queryContext" or "mutateContext"
49
+ */
50
+ export declare function executeDescriptor(descriptor: ContextDescriptor, context: ArcContextAny, adapters: ModelAdapters, contextMethod: "queryContext" | "mutateContext"): any;
51
+ //# sourceMappingURL=context-accessor.d.ts.map
@@ -1,4 +1,8 @@
1
1
  export * from "./model";
2
+ export * from "./model-adapters";
3
+ export * from "./model-like";
4
+ export * from "./scoped-model";
5
+ export * from "./context-accessor";
2
6
  export * from "./mutation-executor";
3
7
  export * from "./live-query";
4
8
  //# sourceMappingURL=index.d.ts.map
@@ -1,32 +1,10 @@
1
1
  import type { ArcContextAny } from "../../context/context";
2
2
  import type { Simplify } from "../../utils";
3
- import type { Model } from "../model";
3
+ /**
4
+ * QueryContext type — maps context element names to their queryContext return types.
5
+ * Used for typing useQuery() accessor.
6
+ */
4
7
  export type QueryContext<C extends ArcContextAny> = Simplify<{
5
8
  [Element in C["elements"][number] as Element["queryContext"] extends (...args: any[]) => infer Return ? Element["name"] : never]: Element["queryContext"] extends (...args: any[]) => infer Return ? Return : never;
6
9
  }>;
7
- /**
8
- * Live Query Result
9
- */
10
- export type LiveQueryResult<T> = {
11
- result: T;
12
- unsubscribe: () => void;
13
- };
14
- /**
15
- * Live Query
16
- *
17
- * Creates a live query that watches views and calls a callback when data changes.
18
- * Uses view's queryContext directly (applies protectBy restrictions).
19
- * Uses dataStorage.observeQueries() for reactivity when available.
20
- * Falls back to streaming mode (SSE) when no local storage.
21
- *
22
- * @example
23
- * ```typescript
24
- * const { result, unsubscribe } = liveQuery(
25
- * model,
26
- * (q) => q.tasks.find({ projectId: "123" }),
27
- * (data) => console.log("Tasks updated:", data)
28
- * );
29
- * ```
30
- */
31
- export declare function liveQuery<C extends ArcContextAny, TResult>(model: Model<C>, queryFn: (q: QueryContext<C>) => Promise<TResult>, callback: (data: TResult) => void): LiveQueryResult<TResult>;
32
10
  //# sourceMappingURL=live-query.d.ts.map
@@ -14,5 +14,10 @@ export type ModelAdapters = {
14
14
  queryWire?: QueryWire;
15
15
  /** Streaming mode cache - used when no local dataStorage */
16
16
  streamingCache?: StreamingQueryCache;
17
+ /** Scope reference — set by ScopedModel for auth injection into wire calls */
18
+ scope?: {
19
+ readonly scopeName: string;
20
+ getToken(): string | null;
21
+ };
17
22
  };
18
23
  //# sourceMappingURL=model-adapters.d.ts.map
@@ -0,0 +1,9 @@
1
+ import type { ArcEnvironment } from "../context-element/context-element";
2
+ import type { ArcContextAny } from "../context/context";
3
+ import type { ModelAdapters } from "./model-adapters";
4
+ export interface ModelLike<C extends ArcContextAny> {
5
+ readonly context: C;
6
+ getAdapters(): ModelAdapters;
7
+ getEnvironment(): ArcEnvironment;
8
+ }
9
+ //# sourceMappingURL=model-like.d.ts.map
@@ -1,11 +1,14 @@
1
1
  import type { ArcEnvironment } from "../context-element/context-element";
2
2
  import type { ArcContextAny } from "../context/context";
3
3
  import type { ModelAdapters } from "./model-adapters";
4
- export declare class Model<Context extends ArcContextAny> {
4
+ import type { ModelLike } from "./model-like";
5
+ import { ScopedModel } from "./scoped-model";
6
+ export declare class Model<Context extends ArcContextAny> implements ModelLike<Context> {
5
7
  readonly context: Context;
6
8
  private adapters;
7
9
  private environment;
8
10
  private initialized;
11
+ private scopes;
9
12
  constructor(context: Context, options: {
10
13
  adapters: ModelAdapters;
11
14
  environment: ArcEnvironment;
@@ -17,5 +20,11 @@ export declare class Model<Context extends ArcContextAny> {
17
20
  init(): Promise<void>;
18
21
  getAdapters(): ModelAdapters;
19
22
  getEnvironment(): ArcEnvironment;
23
+ /**
24
+ * Get or create a named scope.
25
+ * Scopes are cached — same name returns the same instance.
26
+ * For per-request isolation (server), use `new ScopedModel(model, name)` directly.
27
+ */
28
+ scope(name: string): ScopedModel<Context>;
20
29
  }
21
30
  //# sourceMappingURL=model.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import type { ArcContextAny } from "../../context/context";
2
2
  import type { Simplify } from "../../utils";
3
- import type { Model } from "../model";
3
+ import type { ModelLike } from "../model-like";
4
4
  /**
5
5
  * Mutation Executor Type
6
6
  *
@@ -25,5 +25,5 @@ export type MutationExecutor<C extends ArcContextAny> = Simplify<{
25
25
  * await mutations.createTask({ title: "New task" });
26
26
  * ```
27
27
  */
28
- export declare function mutationExecutor<C extends ArcContextAny>(model: Model<C>): MutationExecutor<C>;
28
+ export declare function mutationExecutor<C extends ArcContextAny>(model: ModelLike<C>): MutationExecutor<C>;
29
29
  //# sourceMappingURL=mutation-executor.d.ts.map
@@ -0,0 +1,66 @@
1
+ import { type DecodedToken } from "../adapters/auth-adapter";
2
+ import type { ArcEnvironment } from "../context-element/context-element";
3
+ import type { ArcContextAny } from "../context/context";
4
+ import { type ContextDescriptor } from "./context-accessor";
5
+ import type { ModelAdapters } from "./model-adapters";
6
+ import type { ModelLike } from "./model-like";
7
+ /**
8
+ * ScopedModel — a lightweight wrapper over a parent Model with scope-specific auth.
9
+ *
10
+ * Provides its own AuthAdapter (in-memory), explicit wire methods with auto-injected
11
+ * scope + token (no Proxy), and typed query/command accessors.
12
+ *
13
+ * For long-lived named scopes (client): use `model.scope("workspace")` (cached).
14
+ * For per-request isolation (server): use `new ScopedModel(model, name)` directly.
15
+ */
16
+ export declare class ScopedModel<Context extends ArcContextAny> implements ModelLike<Context> {
17
+ readonly context: Context;
18
+ readonly scopeName: string;
19
+ private readonly parent;
20
+ private readonly authAdapter;
21
+ private readonly scopedAdapters;
22
+ private tokenListeners;
23
+ constructor(parent: ModelLike<Context>, scopeName: string);
24
+ setToken(token: string | null): void;
25
+ getToken(): string | null;
26
+ getDecoded(): DecodedToken | null;
27
+ getParams(): Record<string, any> | null;
28
+ isAuthenticated(): boolean;
29
+ onTokenChange(listener: () => void): () => void;
30
+ getAdapters(): ModelAdapters;
31
+ getEnvironment(): ArcEnvironment;
32
+ private getAuth;
33
+ /**
34
+ * Execute a command via CommandWire with auto-injected scope + token.
35
+ */
36
+ executeCommand(name: string, params: any): Promise<any>;
37
+ /**
38
+ * Execute a remote query via QueryWire with auto-injected scope + token.
39
+ */
40
+ remoteQuery(viewName: string, options?: any): Promise<any[]>;
41
+ /**
42
+ * Subscribe to a server-side query via EventWire with auto-injected scope.
43
+ */
44
+ subscribeQuery(descriptor: ContextDescriptor, callback: (data: any[]) => void): string;
45
+ /**
46
+ * Query descriptor builder. Returns serializable descriptors.
47
+ *
48
+ * Usage: `scope.query.userAccount.find({ where: { _id: "abc" } })`
49
+ */
50
+ get query(): Record<string, Record<string, (...args: any[]) => ContextDescriptor>>;
51
+ /**
52
+ * Command descriptor builder. Returns serializable descriptors.
53
+ *
54
+ * Usage: `scope.command.userAccount.createUser({ name: "Jan" })`
55
+ */
56
+ get command(): Record<string, Record<string, (...args: any[]) => ContextDescriptor>>;
57
+ /**
58
+ * Execute a query descriptor.
59
+ */
60
+ callQuery(descriptor: ContextDescriptor): Promise<any>;
61
+ /**
62
+ * Execute a command descriptor.
63
+ */
64
+ callCommand(descriptor: ContextDescriptor): Promise<any>;
65
+ }
66
+ //# sourceMappingURL=scoped-model.d.ts.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Scoped Wire Proxy - Creates proxy wrappers that auto-inject scope + token
3
+ *
4
+ * The proxy pattern means internal core code (Model, mutationExecutor, liveQuery)
5
+ * doesn't need signature changes — the proxy handles auth at the boundary.
6
+ */
7
+ import type { CommandWire } from "../adapters/command-wire";
8
+ import type { EventWire } from "../adapters/event-wire";
9
+ import type { QueryWire } from "../adapters/query-wire";
10
+ /**
11
+ * Create a proxy around CommandWire that auto-injects scope + token
12
+ */
13
+ export declare function scopedCommandWire(wire: CommandWire, scope: string, getToken: () => string | null): CommandWire;
14
+ /**
15
+ * Create a proxy around QueryWire that auto-injects scope + token
16
+ */
17
+ export declare function scopedQueryWire(wire: QueryWire, scope: string, getToken: () => string | null): QueryWire;
18
+ /**
19
+ * Create a proxy around EventWire that auto-injects scope into subscriptions.
20
+ * subscribeView(viewName, options, callback, scope?) — scope is the 4th arg.
21
+ */
22
+ export declare function scopedEventWire(wire: EventWire, scope: string): EventWire;
23
+ //# sourceMappingURL=scoped-wire-proxy.d.ts.map
@@ -1,6 +1,4 @@
1
1
  export { StreamingQueryCache } from "./streaming-query-cache";
2
2
  export type { CacheChangeListener, StreamingQueryCacheStore, } from "./streaming-query-cache";
3
3
  export { StreamingEventPublisher } from "./streaming-event-publisher";
4
- export { streamingLiveQuery } from "./streaming-live-query";
5
- export type { StreamingLiveQueryOptions } from "./streaming-live-query";
6
4
  //# sourceMappingURL=index.d.ts.map
@@ -1,27 +1,25 @@
1
1
  /**
2
2
  * StreamingLiveQuery - Live query implementation for streaming mode
3
3
  *
4
- * Uses SSE stream to get initial data and updates from server.
5
- * Uses StreamingQueryCache for local state and reactivity.
6
- * Deduplicates SSE streams - multiple queries on same view share one stream.
7
- * Static views are handled directly without SSE.
4
+ * Uses WebSocket view subscriptions for both initial data and live updates.
5
+ * Server runs liveQuery on view tables and pushes results via WS.
6
+ * Static views are handled directly without network.
8
7
  */
9
- import type { QueryWire } from "../adapters/query-wire";
8
+ import type { EventWire } from "../adapters/event-wire";
10
9
  import type { ArcContextAny } from "../context/context";
11
10
  import type { LiveQueryResult, QueryContext } from "../model/live-query/live-query";
12
- import type { Model } from "../model/model";
11
+ import type { ModelLike } from "../model/model-like";
13
12
  import type { StreamingQueryCache } from "./streaming-query-cache";
14
13
  export interface StreamingLiveQueryOptions {
15
- queryWire: QueryWire;
16
14
  cache: StreamingQueryCache;
17
- authToken?: string | null;
15
+ eventWire: EventWire;
18
16
  }
19
17
  /**
20
18
  * Create a streaming live query
21
19
  *
22
- * Opens SSE stream to server, populates cache, and reacts to changes.
23
- * Reuses existing streams for the same view (deduplication via cache).
24
- * Static views return data directly without SSE.
20
+ * Subscribes to server-side view queries via WebSocket.
21
+ * Server runs liveQuery and pushes view-data messages on change.
22
+ * Static views return data directly without network.
25
23
  */
26
- export declare function streamingLiveQuery<C extends ArcContextAny, TResult>(model: Model<C>, queryFn: (q: QueryContext<C>) => Promise<TResult>, callback: (data: TResult) => void, options: StreamingLiveQueryOptions): LiveQueryResult<TResult>;
24
+ export declare function streamingLiveQuery<C extends ArcContextAny, TResult>(model: ModelLike<C>, queryFn: (q: QueryContext<C>) => Promise<TResult>, callback: (data: TResult) => void, options: StreamingLiveQueryOptions): LiveQueryResult<TResult>;
27
25
  //# sourceMappingURL=streaming-live-query.d.ts.map
@@ -11,17 +11,24 @@
11
11
  * - Receives updates from SSE stream
12
12
  * - Deduplicates SSE streams (one stream per view)
13
13
  */
14
+ import type { EventWire } from "../adapters/event-wire";
14
15
  import type { ArcEventAny } from "../context-element/event/event";
15
16
  import type { ArcEventInstance } from "../context-element/event/instance";
16
17
  import type { ArcViewAny } from "../context-element/view/view";
17
18
  import type { FindOptions } from "../data-storage/find-options";
18
- export type CacheChangeListener = () => void;
19
+ import type { ListenerEvent } from "../data-storage/data-storage.abstract";
20
+ /**
21
+ * Cache change listener receives ListenerEvent[] for incremental changes,
22
+ * or null for bulk replacement (setAll) which requires full re-query.
23
+ */
24
+ export type CacheChangeListener = (events: ListenerEvent<any>[] | null) => void;
19
25
  export interface StreamingQueryCacheStore<Item extends {
20
26
  _id: string;
21
27
  }> {
22
28
  find(options?: FindOptions<Item>): Item[];
23
29
  findOne(where?: Record<string, any>): Item | undefined;
24
30
  subscribe(listener: CacheChangeListener): () => void;
31
+ hasData(): boolean;
25
32
  }
26
33
  /**
27
34
  * StreamingQueryCache - Main cache class
@@ -30,6 +37,8 @@ export declare class StreamingQueryCache {
30
37
  private stores;
31
38
  private views;
32
39
  private activeStreams;
40
+ private pendingUnsubscribes;
41
+ private static UNSUBSCRIBE_DELAY_MS;
33
42
  /**
34
43
  * Register views that this cache will handle
35
44
  */
@@ -59,15 +68,27 @@ export declare class StreamingQueryCache {
59
68
  wasReused: boolean;
60
69
  };
61
70
  /**
62
- * Unregister from a stream (decrement ref count, close if zero)
71
+ * Unregister from a stream. When refCount hits 0, delays actual WS
72
+ * unsubscribe by UNSUBSCRIBE_DELAY_MS. If re-registered within the
73
+ * window, the existing subscription is reused (cache serves immediately).
63
74
  */
64
75
  private unregisterStream;
65
76
  /**
66
- * Set initial data for a view (from SSE stream)
77
+ * Subscribe to a query via WebSocket with deduplication.
78
+ * Multiple callers share a single WS subscription per descriptor key.
79
+ * Returns unsubscribe function that decrements refcount.
80
+ */
81
+ subscribeQuery(descriptor: {
82
+ element: string;
83
+ method: string;
84
+ args: any[];
85
+ }, eventWire: EventWire, scope?: string): () => void;
86
+ /**
87
+ * Set data for a view. Accepts array or single item (from queryMethod findOne).
67
88
  */
68
89
  setViewData<Item extends {
69
90
  _id: string;
70
- }>(viewName: string, items: Item[]): void;
91
+ }>(viewName: string, data: Item[] | Item | undefined | null): void;
71
92
  /**
72
93
  * Apply an event to update view state
73
94
  * Runs view handlers to update the cache
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc",
3
3
  "type": "module",
4
- "version": "0.3.6",
4
+ "version": "0.4.1",
5
5
  "private": false,
6
6
  "author": "Przemysław Krasiński [arcote.tech]",
7
7
  "description": "Arc framework core rewrite with improved event emission and type safety",