@kontsedal/olas-core 0.0.1-rc.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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/dist/index.cjs +363 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +178 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +178 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.mjs +339 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/dist/root-BImHnGj1.mjs +3270 -0
  12. package/dist/root-BImHnGj1.mjs.map +1 -0
  13. package/dist/root-Bazp5_Ik.cjs +3347 -0
  14. package/dist/root-Bazp5_Ik.cjs.map +1 -0
  15. package/dist/testing.cjs +81 -0
  16. package/dist/testing.cjs.map +1 -0
  17. package/dist/testing.d.cts +56 -0
  18. package/dist/testing.d.cts.map +1 -0
  19. package/dist/testing.d.mts +56 -0
  20. package/dist/testing.d.mts.map +1 -0
  21. package/dist/testing.mjs +78 -0
  22. package/dist/testing.mjs.map +1 -0
  23. package/dist/types-CAMgqCMz.d.mts +816 -0
  24. package/dist/types-CAMgqCMz.d.mts.map +1 -0
  25. package/dist/types-emq_lZd7.d.cts +816 -0
  26. package/dist/types-emq_lZd7.d.cts.map +1 -0
  27. package/package.json +47 -0
  28. package/src/__dev__.d.ts +8 -0
  29. package/src/controller/define.ts +50 -0
  30. package/src/controller/index.ts +12 -0
  31. package/src/controller/instance.ts +499 -0
  32. package/src/controller/root.ts +160 -0
  33. package/src/controller/types.ts +195 -0
  34. package/src/devtools.ts +0 -0
  35. package/src/emitter.ts +79 -0
  36. package/src/errors.ts +49 -0
  37. package/src/forms/field.ts +303 -0
  38. package/src/forms/form-types.ts +130 -0
  39. package/src/forms/form.ts +640 -0
  40. package/src/forms/index.ts +2 -0
  41. package/src/forms/types.ts +1 -0
  42. package/src/forms/validators.ts +70 -0
  43. package/src/index.ts +89 -0
  44. package/src/query/client.ts +934 -0
  45. package/src/query/define.ts +154 -0
  46. package/src/query/entry.ts +322 -0
  47. package/src/query/focus-online.ts +73 -0
  48. package/src/query/index.ts +3 -0
  49. package/src/query/infinite.ts +462 -0
  50. package/src/query/keys.ts +33 -0
  51. package/src/query/local.ts +113 -0
  52. package/src/query/mutation.ts +384 -0
  53. package/src/query/plugin.ts +135 -0
  54. package/src/query/types.ts +168 -0
  55. package/src/query/use.ts +321 -0
  56. package/src/scope.ts +42 -0
  57. package/src/selection.ts +146 -0
  58. package/src/signals/index.ts +3 -0
  59. package/src/signals/readonly.ts +22 -0
  60. package/src/signals/runtime.ts +115 -0
  61. package/src/signals/types.ts +31 -0
  62. package/src/testing.ts +142 -0
  63. package/src/timing/debounced.ts +32 -0
  64. package/src/timing/index.ts +2 -0
  65. package/src/timing/throttled.ts +46 -0
  66. package/src/utils.ts +13 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/signals/runtime.ts","../src/controller/define.ts","../src/controller/root.ts","../src/forms/validators.ts","../src/forms/field.ts","../src/query/define.ts","../src/selection.ts","../src/timing/debounced.ts","../src/timing/throttled.ts","../src/utils.ts"],"mappings":";;;;;AAsEA;;;;iBAAgB,MAAA,GAAA,CAAU,OAAA,EAAS,CAAA,GAAI,MAAA,CAAO,CAAA;;;;;;;;;iBAY9B,QAAA,GAAA,CAAY,EAAA,QAAU,CAAA,GAAI,QAAA,CAAS,CAAA;AAZJ;AAY/C;;;;;;;AAZ+C,iBAwB/B,MAAA,CAAO,EAA6B;;;;;iBAQpC,KAAA,GAAA,CAAS,EAAA,QAAU,CAAA,GAAI,CAAC;;AApBY;AAYpD;;;;iBAkBgB,SAAA,GAAA,CAAa,EAAA,QAAU,CAAA,GAAI,CAAC;;;;KCxGhC,uBAAA;ED8DI;;;;;;;;ECrDd,IAAI;AAAA;;;;;ADqDyC;AAY/C;;iBCvDgB,gBAAA,6BAAA,CACd,OAAA,GAAU,GAAA,EAAK,GAAA,EAAK,KAAA,EAAO,KAAA,KAAU,GAAA,EACrC,OAAA,GAAU,uBAAA,GACT,aAAA,CAAc,KAAA,EAAO,GAAA;;;;;;;iBC4HR,UAAA,oBAA8B,MAAA,oBAA0B,WAAA,CAAA,CACtE,GAAA,EAAK,aAAA,OAAoB,GAAA,GACzB,OAAA,EAAS,WAAA,CAAY,KAAA,IACpB,IAAA,CAAK,GAAA;;;;cCnJK,QAAA,MACP,OAAA,cAAuB,SAAS,CAAC,CAAA;AH2DvC;AAAA,cGtDa,SAAA,GACV,CAAA,UAAW,OAAA,cAAmB,SAAS;;cAQ7B,SAAA,GACV,CAAA,UAAW,OAAA,cAAmB,SAAS;;cAQ7B,GAAA,GACV,CAAA,UAAW,OAAA,cAAmB,SAAS;;cAQ7B,GAAA,GACV,CAAA,UAAW,OAAA,cAAmB,SAAS;;cAY7B,KAAA,GACV,OAAA,cAAoC,SAAS;;cAOnC,OAAA,GACV,EAAA,EAAI,MAAA,EAAQ,OAAA,cAA6B,SAAS;;;;;;;;iBCwNrC,kBAAA,GAAA,CACd,EAAA,GAAK,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,WAAA,KAAgB,OAAA,iBACvC,EAAA,WACC,SAAA,CAAU,CAAA;;;;AJtNb;;;iBKtCgB,WAAA,2BAAA,CAAuC,IAAA,EAAM,SAAA,CAAU,IAAA,EAAM,CAAA,IAAK,KAAA,CAAM,IAAA,EAAM,CAAA;;;;;;;;iBAkE9E,mBAAA,mDAAsE,KAAA,CAAA,CACpF,IAAA,EAAM,iBAAA,CAAkB,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,KAAA,IAC/C,aAAA,CAAc,IAAA,EAAM,KAAA,EAAO,KAAA;;;;;AL9B9B;;;;;KM1DY,SAAA;EACV,WAAA,EAAa,UAAA,CAAW,WAAA;EACxB,IAAA,EAAM,UAAA;EACN,UAAA,CAAW,EAAA,WAAa,UAAA;EAExB,MAAA,CAAO,EAAA;EACP,QAAA,CAAS,EAAA;EACT,MAAA,CAAO,EAAA;EACP,KAAA;EACA,SAAA,CAAU,GAAA;EAEV,WAAA,CACE,EAAA,UACA,IAAA;IAAQ,KAAA;IAAiB,IAAA;EAAA,GACzB,OAAA;AAAA;;;;;;;;;;;ANwDgD;iBMzCpC,SAAA,aAAA,CAAuB,OAAA;EAAY,OAAA;AAAA,IAAgC,SAAS,CAAC,CAAA;;;;;AN6B7F;;;;;iBO5DgB,SAAA,GAAA,CAAa,MAAA,EAAQ,UAAA,CAAW,CAAA,GAAI,EAAA,WAAa,UAAA,CAAW,CAAA;;;;;AP4D5E;;;;;iBQ5DgB,SAAA,GAAA,CAAa,MAAA,EAAQ,UAAA,CAAW,CAAA,GAAI,EAAA,WAAa,UAAA,CAAW,CAAA;;;;;;AR4D5E;;;;iBS/DgB,YAAA,CAAa,GAAY"}
@@ -0,0 +1,178 @@
1
+ import { $ as Validator, A as DehydratedEntry, B as DeepPartial, C as DebugCacheEntry, D as InfiniteQuerySubscription, E as InfiniteQuerySpec, F as QuerySubscription, G as FieldArrayValue, H as FieldArrayItemErrors, I as RetryDelay, J as FormOptions, K as Form, L as RetryPolicy, M as LocalCache, N as Query, O as AsyncState, P as QuerySpec, Q as ItemInitial, R as Snapshot, S as DebugBus, T as InfiniteQuery, U as FieldArrayOptions, V as FieldArray, W as FieldArrayValidator, X as FormValidator, Y as FormSchema, Z as FormValue, _ as SetDataEvent, a as Ctx, at as createEmitter, b as MutationConcurrency, c as RootOptions, d as defineScope, et as Computed, f as GcEvent, g as RegisteredQuery, h as QueryClientPluginApi, i as CtrlProps, it as Emitter, j as DehydratedState, k as AsyncStatus, l as Scope, m as QueryClientPlugin, n as ControllerDef, nt as Signal, o as Field, p as InvalidateEvent, q as FormErrors, r as CtrlApi, rt as ErrorContext, s as Root, t as AmbientDeps, tt as ReadSignal, u as ScopeOptions, v as lookupRegisteredQuery, w as DebugEvent, x as MutationSpec, y as Mutation, z as UseOptions } from "./types-CAMgqCMz.mjs";
2
+
3
+ //#region src/signals/runtime.d.ts
4
+ /**
5
+ * Create a writable `Signal<T>`. Reads track the current auto-tracking scope
6
+ * (effect / computed); writes notify all subscribers (deduped via `Object.is`).
7
+ *
8
+ * Spec §20.1. For a single-pass non-tracked read use `signal.peek()`.
9
+ */
10
+ declare function signal<T>(initial: T): Signal<T>;
11
+ /**
12
+ * Create a `Computed<T>` — a read-only derived signal. The provided `fn` is
13
+ * re-evaluated whenever a signal it read during its last run changes; the
14
+ * resulting value is cached until then.
15
+ *
16
+ * Spec §20.1. The graph is glitch-free: a `computed` re-runs at most once per
17
+ * batched-write cycle.
18
+ */
19
+ declare function computed<T>(fn: () => T): Computed<T>;
20
+ /**
21
+ * Run `fn` immediately and again whenever any signal it reads changes. If
22
+ * `fn` returns a function, that function is called as a cleanup before the
23
+ * next re-run and on dispose.
24
+ *
25
+ * Returns a `dispose` function. Inside a controller use `ctx.effect(...)`
26
+ * instead — that variant is auto-disposed with the controller.
27
+ */
28
+ declare function effect(fn: () => void | (() => void)): () => void;
29
+ /**
30
+ * Batch synchronous signal writes so subscribers see one notification at the
31
+ * end of the batch rather than one per write. Returns whatever `fn` returns.
32
+ */
33
+ declare function batch<T>(fn: () => T): T;
34
+ /**
35
+ * Run `fn` with auto-tracking suppressed — signals read inside don't become
36
+ * dependencies of the surrounding `computed` / `effect`. Useful for "read
37
+ * these signals once to log them" or for snapshotting state inside an effect
38
+ * without subscribing to it. For a single-signal peek, prefer `signal.peek()`.
39
+ */
40
+ declare function untracked<T>(fn: () => T): T;
41
+ //#endregion
42
+ //#region src/controller/define.d.ts
43
+ /** Optional configuration for `defineController`. */
44
+ type DefineControllerOptions = {
45
+ /**
46
+ * A short, human-readable name for this controller — used in the devtools
47
+ * tree, `controller:*` events, and error contexts (e.g. `["root","board[0]"]`).
48
+ *
49
+ * When omitted, the runtime falls back to `factory.name` (the JS-inferred
50
+ * function name) or `"anonymous"` for arrow-function factories defined
51
+ * inline. Naming is strongly recommended in app code.
52
+ */
53
+ name?: string;
54
+ };
55
+ /**
56
+ * Create a controller definition. The factory is stored on the returned object
57
+ * and invoked during `createRoot` / `ctx.child` to build instances.
58
+ *
59
+ * `Props` defaults to `void` so a factory written as `(ctx) => ...` is typed
60
+ * as `ControllerDef<void, Api>` — the form `createRoot` requires.
61
+ */
62
+ declare function defineController<Props = void, Api = unknown>(factory: (ctx: Ctx, props: Props) => Api, options?: DefineControllerOptions): ControllerDef<Props, Api>;
63
+ //#endregion
64
+ //#region src/controller/root.d.ts
65
+ /**
66
+ * Construct a root controller. Root factories take no props — startup config
67
+ * goes in `deps`.
68
+ */
69
+ declare function createRoot<Api, TDeps extends Record<string, unknown> = AmbientDeps>(def: ControllerDef<void, Api>, options: RootOptions<TDeps>): Root<Api>;
70
+ //#endregion
71
+ //#region src/forms/validators.d.ts
72
+ /** Reject empty values (undefined, null, empty string, empty array). */
73
+ declare const required: <T>(message?: string) => Validator<T>;
74
+ /** Reject strings / arrays shorter than `n`. Allows null/undefined (use with `required` to forbid). */
75
+ declare const minLength: (n: number, message?: string) => Validator<string | readonly unknown[]>;
76
+ /** Reject strings / arrays longer than `n`. */
77
+ declare const maxLength: (n: number, message?: string) => Validator<string | readonly unknown[]>;
78
+ /** Reject numbers less than `n`. */
79
+ declare const min: (n: number, message?: string) => Validator<number>;
80
+ /** Reject numbers greater than `n`. */
81
+ declare const max: (n: number, message?: string) => Validator<number>;
82
+ /** Reject strings that don't look like an email. Empty / null pass (use with `required` to forbid). */
83
+ declare const email: (message?: string) => Validator<string>;
84
+ /** Reject strings that don't match the supplied `RegExp`. */
85
+ declare const pattern: (re: RegExp, message?: string) => Validator<string>;
86
+ //#endregion
87
+ //#region src/forms/field.d.ts
88
+ /**
89
+ * Wrap an async validator with a debounce. The debounce timer resets on every
90
+ * value change. While debouncing or the request is in flight, the field's
91
+ * `isValidating` is true and `isValid` is false (treat-as-invalid-until-proven-valid).
92
+ */
93
+ declare function debouncedValidator<T>(fn: (value: T, signal: AbortSignal) => Promise<string | null>, ms: number): Validator<T>;
94
+ //#endregion
95
+ //#region src/query/define.d.ts
96
+ /**
97
+ * Define a keyed, shared query. The returned Query value lives at module
98
+ * scope; per-root QueryClients bind their own entry registries to it.
99
+ */
100
+ declare function defineQuery<Args extends unknown[], T>(spec: QuerySpec<Args, T>): Query<Args, T>;
101
+ /**
102
+ * Define a paginated query (chat-style "load more", infinite scrolling). Pages
103
+ * are kept in order and concatenated via `getNextPageParam` /
104
+ * `getPreviousPageParam`. The returned handle is module-scoped — bind
105
+ * subscribers via `ctx.use(infiniteQuery, () => [...args])`. Spec §5.7,
106
+ * §20.4.
107
+ */
108
+ declare function defineInfiniteQuery<Args extends unknown[], PageParam, TPage, TItem = TPage>(spec: InfiniteQuerySpec<Args, PageParam, TPage, TItem>): InfiniteQuery<Args, TPage, TItem>;
109
+ //#endregion
110
+ //#region src/selection.d.ts
111
+ /**
112
+ * Multi-select state for tables / lists with bulk actions (spec §17.5).
113
+ *
114
+ * Plain function — not bound to `ctx`. Place it in a controller's closure so
115
+ * it dies with the closure. The phantom `T` parameter brands the selection by
116
+ * item type; IDs are always strings.
117
+ */
118
+ type Selection<T = unknown> = {
119
+ selectedIds: ReadSignal<ReadonlySet<string>>;
120
+ size: ReadSignal<number>;
121
+ isSelected(id: string): ReadSignal<boolean>;
122
+ select(id: string): void;
123
+ deselect(id: string): void;
124
+ toggle(id: string): void;
125
+ clear(): void;
126
+ selectAll(ids: readonly string[]): void;
127
+ handleClick(id: string, mods: {
128
+ shift?: boolean;
129
+ meta?: boolean;
130
+ }, ordered: readonly string[]): void;
131
+ };
132
+ /**
133
+ * Create a `Selection<T>`. Optional `initial` seeds the selected set.
134
+ *
135
+ * `handleClick` encapsulates the standard click semantics:
136
+ * - plain click → select only `id` (anchor moves to `id`)
137
+ * - meta-click → toggle `id` (anchor moves to `id` on add)
138
+ * - shift-click → range from anchor to `id` along `ordered` (anchor sticks,
139
+ * so subsequent shift-clicks extend from the same origin)
140
+ *
141
+ * Spec §16.5 / §17.5.
142
+ */
143
+ declare function selection<T = unknown>(options?: {
144
+ initial?: readonly string[];
145
+ }): Selection<T>;
146
+ //#endregion
147
+ //#region src/timing/debounced.d.ts
148
+ /**
149
+ * Lag a signal by `ms`. The returned signal updates only after the source has
150
+ * been unchanged for `ms`. Each new write resets the timer.
151
+ *
152
+ * No lifecycle — the internal effect runs for the lifetime of the program.
153
+ * Use inside a controller closure so it dies with the closure.
154
+ */
155
+ declare function debounced<T>(source: ReadSignal<T>, ms: number): ReadSignal<T>;
156
+ //#endregion
157
+ //#region src/timing/throttled.d.ts
158
+ /**
159
+ * Rate-limit a signal so it emits at most once per `ms` (leading + trailing).
160
+ * The first change passes through immediately. Subsequent changes within the
161
+ * window are coalesced; the latest value is emitted when the window expires.
162
+ *
163
+ * No lifecycle — see debounced() note.
164
+ */
165
+ declare function throttled<T>(source: ReadSignal<T>, ms: number): ReadSignal<T>;
166
+ //#endregion
167
+ //#region src/utils.d.ts
168
+ /**
169
+ * True iff `err` is an AbortError. Used to filter superseded latest-wins
170
+ * mutations and aborted fetches from genuine failures.
171
+ *
172
+ * Spec: §20.12 — checks `err instanceof DOMException && err.name === 'AbortError'`.
173
+ * Node 17+ exposes a global DOMException, so this works server-side too.
174
+ */
175
+ declare function isAbortError(err: unknown): boolean;
176
+ //#endregion
177
+ export { type AmbientDeps, type AsyncState, type AsyncStatus, type Computed, type ControllerDef, type CtrlApi, type CtrlProps, type Ctx, type DebugBus, type DebugCacheEntry, type DebugEvent, type DeepPartial, type DehydratedEntry, type DehydratedState, type Emitter, type ErrorContext, type Field, type FieldArray, type FieldArrayItemErrors, type FieldArrayOptions, type FieldArrayValidator, type FieldArrayValue, type Form, type FormErrors, type FormOptions, type FormSchema, type FormValidator, type FormValue, type GcEvent, type InfiniteQuery, type InfiniteQuerySpec, type InfiniteQuerySubscription, type InvalidateEvent, type ItemInitial, type LocalCache, type Mutation, type MutationConcurrency, type MutationSpec, type Query, type QueryClientPlugin, type QueryClientPluginApi, type QuerySpec, type QuerySubscription, type ReadSignal, type RegisteredQuery, type RetryDelay, type RetryPolicy, type Root, type RootOptions, type Scope, type ScopeOptions, type Selection, type SetDataEvent, type Signal, type Snapshot, type UseOptions, type Validator, batch, computed, createEmitter, createRoot, debounced, debouncedValidator, defineController, defineInfiniteQuery, defineQuery, defineScope, effect, email, isAbortError, lookupRegisteredQuery, max, maxLength, min, minLength, pattern, required, selection, signal, throttled, untracked };
178
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/signals/runtime.ts","../src/controller/define.ts","../src/controller/root.ts","../src/forms/validators.ts","../src/forms/field.ts","../src/query/define.ts","../src/selection.ts","../src/timing/debounced.ts","../src/timing/throttled.ts","../src/utils.ts"],"mappings":";;;;;AAsEA;;;;iBAAgB,MAAA,GAAA,CAAU,OAAA,EAAS,CAAA,GAAI,MAAA,CAAO,CAAA;;;;;;;;;iBAY9B,QAAA,GAAA,CAAY,EAAA,QAAU,CAAA,GAAI,QAAA,CAAS,CAAA;AAZJ;AAY/C;;;;;;;AAZ+C,iBAwB/B,MAAA,CAAO,EAA6B;;;;;iBAQpC,KAAA,GAAA,CAAS,EAAA,QAAU,CAAA,GAAI,CAAC;;AApBY;AAYpD;;;;iBAkBgB,SAAA,GAAA,CAAa,EAAA,QAAU,CAAA,GAAI,CAAC;;;;KCxGhC,uBAAA;ED8DI;;;;;;;;ECrDd,IAAI;AAAA;;;;;ADqDyC;AAY/C;;iBCvDgB,gBAAA,6BAAA,CACd,OAAA,GAAU,GAAA,EAAK,GAAA,EAAK,KAAA,EAAO,KAAA,KAAU,GAAA,EACrC,OAAA,GAAU,uBAAA,GACT,aAAA,CAAc,KAAA,EAAO,GAAA;;;;;;;iBC4HR,UAAA,oBAA8B,MAAA,oBAA0B,WAAA,CAAA,CACtE,GAAA,EAAK,aAAA,OAAoB,GAAA,GACzB,OAAA,EAAS,WAAA,CAAY,KAAA,IACpB,IAAA,CAAK,GAAA;;;;cCnJK,QAAA,MACP,OAAA,cAAuB,SAAS,CAAC,CAAA;AH2DvC;AAAA,cGtDa,SAAA,GACV,CAAA,UAAW,OAAA,cAAmB,SAAS;;cAQ7B,SAAA,GACV,CAAA,UAAW,OAAA,cAAmB,SAAS;;cAQ7B,GAAA,GACV,CAAA,UAAW,OAAA,cAAmB,SAAS;;cAQ7B,GAAA,GACV,CAAA,UAAW,OAAA,cAAmB,SAAS;;cAY7B,KAAA,GACV,OAAA,cAAoC,SAAS;;cAOnC,OAAA,GACV,EAAA,EAAI,MAAA,EAAQ,OAAA,cAA6B,SAAS;;;;;;;;iBCwNrC,kBAAA,GAAA,CACd,EAAA,GAAK,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,WAAA,KAAgB,OAAA,iBACvC,EAAA,WACC,SAAA,CAAU,CAAA;;;;AJtNb;;;iBKtCgB,WAAA,2BAAA,CAAuC,IAAA,EAAM,SAAA,CAAU,IAAA,EAAM,CAAA,IAAK,KAAA,CAAM,IAAA,EAAM,CAAA;;;;;;;;iBAkE9E,mBAAA,mDAAsE,KAAA,CAAA,CACpF,IAAA,EAAM,iBAAA,CAAkB,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,KAAA,IAC/C,aAAA,CAAc,IAAA,EAAM,KAAA,EAAO,KAAA;;;;;AL9B9B;;;;;KM1DY,SAAA;EACV,WAAA,EAAa,UAAA,CAAW,WAAA;EACxB,IAAA,EAAM,UAAA;EACN,UAAA,CAAW,EAAA,WAAa,UAAA;EAExB,MAAA,CAAO,EAAA;EACP,QAAA,CAAS,EAAA;EACT,MAAA,CAAO,EAAA;EACP,KAAA;EACA,SAAA,CAAU,GAAA;EAEV,WAAA,CACE,EAAA,UACA,IAAA;IAAQ,KAAA;IAAiB,IAAA;EAAA,GACzB,OAAA;AAAA;;;;;;;;;;;ANwDgD;iBMzCpC,SAAA,aAAA,CAAuB,OAAA;EAAY,OAAA;AAAA,IAAgC,SAAS,CAAC,CAAA;;;;;AN6B7F;;;;;iBO5DgB,SAAA,GAAA,CAAa,MAAA,EAAQ,UAAA,CAAW,CAAA,GAAI,EAAA,WAAa,UAAA,CAAW,CAAA;;;;;AP4D5E;;;;;iBQ5DgB,SAAA,GAAA,CAAa,MAAA,EAAQ,UAAA,CAAW,CAAA,GAAI,EAAA,WAAa,UAAA,CAAW,CAAA;;;;;;AR4D5E;;;;iBS/DgB,YAAA,CAAa,GAAY"}
package/dist/index.mjs ADDED
@@ -0,0 +1,339 @@
1
+ import { a as lookupRegisteredQuery, c as batch, d as signal, f as untracked, i as createEmitter, l as computed, o as registerQueryById, p as defineController, r as debouncedValidator, s as isAbortError, t as createRoot, u as effect } from "./root-BImHnGj1.mjs";
2
+ //#region src/signals/readonly.ts
3
+ /**
4
+ * Project a Signal (or any object with a reactive `value` + `peek` + `subscribe`)
5
+ * as a `ReadSignal`. The returned object does not expose `set` / `update` /
6
+ * settable `value`, so it can be returned from APIs without callers mutating it.
7
+ *
8
+ * Internal helper — not exported from the package's public surface.
9
+ */
10
+ function readOnly(source) {
11
+ return {
12
+ get value() {
13
+ return source.value;
14
+ },
15
+ peek() {
16
+ return source.peek();
17
+ },
18
+ subscribe(handler) {
19
+ return source.subscribe(handler);
20
+ }
21
+ };
22
+ }
23
+ //#endregion
24
+ //#region src/forms/validators.ts
25
+ const isEmpty = (value) => {
26
+ if (value === void 0 || value === null) return true;
27
+ if (typeof value === "string") return value.length === 0;
28
+ if (Array.isArray(value)) return value.length === 0;
29
+ return false;
30
+ };
31
+ /** Reject empty values (undefined, null, empty string, empty array). */
32
+ const required = (message = "Required") => (value) => isEmpty(value) ? message : null;
33
+ /** Reject strings / arrays shorter than `n`. Allows null/undefined (use with `required` to forbid). */
34
+ const minLength = (n, message) => (value) => {
35
+ if (value == null) return null;
36
+ if (value.length >= n) return null;
37
+ return message ?? `Must be at least ${n} characters`;
38
+ };
39
+ /** Reject strings / arrays longer than `n`. */
40
+ const maxLength = (n, message) => (value) => {
41
+ if (value == null) return null;
42
+ if (value.length <= n) return null;
43
+ return message ?? `Must be no more than ${n} characters`;
44
+ };
45
+ /** Reject numbers less than `n`. */
46
+ const min = (n, message) => (value) => {
47
+ if (value == null) return null;
48
+ if (value >= n) return null;
49
+ return message ?? `Must be at least ${n}`;
50
+ };
51
+ /** Reject numbers greater than `n`. */
52
+ const max = (n, message) => (value) => {
53
+ if (value == null) return null;
54
+ if (value <= n) return null;
55
+ return message ?? `Must be no more than ${n}`;
56
+ };
57
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
58
+ /** Reject strings that don't look like an email. Empty / null pass (use with `required` to forbid). */
59
+ const email = (message = "Invalid email address") => (value) => {
60
+ if (value == null || value === "") return null;
61
+ return EMAIL_RE.test(value) ? null : message;
62
+ };
63
+ /** Reject strings that don't match the supplied `RegExp`. */
64
+ const pattern = (re, message = "Invalid format") => (value) => {
65
+ if (value == null || value === "") return null;
66
+ return re.test(value) ? null : message;
67
+ };
68
+ //#endregion
69
+ //#region src/query/define.ts
70
+ function registerQueryId(spec, query) {
71
+ if (spec.queryId != null) registerQueryById(spec.queryId, query);
72
+ else if (spec.crossTab === true) {}
73
+ }
74
+ /**
75
+ * Define a keyed, shared query. The returned Query value lives at module
76
+ * scope; per-root QueryClients bind their own entry registries to it.
77
+ */
78
+ function defineQuery(spec) {
79
+ const clients = /* @__PURE__ */ new Set();
80
+ const query = {
81
+ __olas: "query",
82
+ __spec: spec,
83
+ __clients: clients,
84
+ invalidate(...args) {
85
+ for (const client of clients) client.invalidate(query, args);
86
+ },
87
+ invalidateAll() {
88
+ for (const client of clients) client.invalidateAll(query);
89
+ },
90
+ setData(...rest) {
91
+ const updater = rest[rest.length - 1];
92
+ const keyArgs = rest.slice(0, -1);
93
+ const childSnapshots = [];
94
+ for (const client of clients) childSnapshots.push(client.setData(query, keyArgs, updater));
95
+ return {
96
+ rollback: () => {
97
+ for (const s of childSnapshots) s.rollback();
98
+ },
99
+ finalize: () => {
100
+ for (const s of childSnapshots) s.finalize();
101
+ }
102
+ };
103
+ },
104
+ prefetch(...args) {
105
+ const [first] = clients;
106
+ if (!first) return Promise.reject(/* @__PURE__ */ new Error("[olas] prefetch called before any root has subscribed"));
107
+ return first.prefetch(query, args);
108
+ }
109
+ };
110
+ registerQueryId(spec, query);
111
+ return query;
112
+ }
113
+ /**
114
+ * Define a paginated query (chat-style "load more", infinite scrolling). Pages
115
+ * are kept in order and concatenated via `getNextPageParam` /
116
+ * `getPreviousPageParam`. The returned handle is module-scoped — bind
117
+ * subscribers via `ctx.use(infiniteQuery, () => [...args])`. Spec §5.7,
118
+ * §20.4.
119
+ */
120
+ function defineInfiniteQuery(spec) {
121
+ const clients = /* @__PURE__ */ new Set();
122
+ const query = {
123
+ __olas: "infiniteQuery",
124
+ __spec: spec,
125
+ __clients: clients,
126
+ invalidate(...args) {
127
+ for (const client of clients) client.invalidateInfinite(query, args);
128
+ },
129
+ invalidateAll() {
130
+ for (const client of clients) client.invalidateAllInfinite(query);
131
+ },
132
+ setData(...rest) {
133
+ const updater = rest[rest.length - 1];
134
+ const keyArgs = rest.slice(0, -1);
135
+ const childSnapshots = [];
136
+ for (const client of clients) childSnapshots.push(client.setInfiniteData(query, keyArgs, updater));
137
+ return {
138
+ rollback: () => {
139
+ for (const s of childSnapshots) s.rollback();
140
+ },
141
+ finalize: () => {
142
+ for (const s of childSnapshots) s.finalize();
143
+ }
144
+ };
145
+ },
146
+ prefetch(...args) {
147
+ const [first] = clients;
148
+ if (!first) return Promise.reject(/* @__PURE__ */ new Error("[olas] prefetch called before any root has subscribed"));
149
+ return first.prefetchInfinite(query, args);
150
+ }
151
+ };
152
+ registerQueryId(spec, query);
153
+ return query;
154
+ }
155
+ //#endregion
156
+ //#region src/scope.ts
157
+ /**
158
+ * Create a scope. The returned value is the typed handle passed to
159
+ * `ctx.provide(scope, value)` and `ctx.inject(scope)`. Identity is keyed by
160
+ * an internal symbol so two `defineScope()` calls — even with identical
161
+ * options — yield distinct scopes.
162
+ */
163
+ function defineScope(options) {
164
+ const hasDefault = options !== void 0 && "default" in options;
165
+ const name = options?.name;
166
+ return {
167
+ __olas: "scope",
168
+ __id: Symbol(name ?? "scope"),
169
+ hasDefault,
170
+ ...name !== void 0 ? { name } : {},
171
+ ...hasDefault ? { default: options?.default } : {}
172
+ };
173
+ }
174
+ //#endregion
175
+ //#region src/selection.ts
176
+ /**
177
+ * Create a `Selection<T>`. Optional `initial` seeds the selected set.
178
+ *
179
+ * `handleClick` encapsulates the standard click semantics:
180
+ * - plain click → select only `id` (anchor moves to `id`)
181
+ * - meta-click → toggle `id` (anchor moves to `id` on add)
182
+ * - shift-click → range from anchor to `id` along `ordered` (anchor sticks,
183
+ * so subsequent shift-clicks extend from the same origin)
184
+ *
185
+ * Spec §16.5 / §17.5.
186
+ */
187
+ function selection(options) {
188
+ const ids = signal(new Set(options?.initial));
189
+ let anchor = options?.initial?.length ? options.initial[options.initial.length - 1] ?? null : null;
190
+ let preShiftSelection = null;
191
+ const size = computed(() => ids.value.size);
192
+ const isSelected = (id) => computed(() => ids.value.has(id));
193
+ const select = (id) => {
194
+ const prev = ids.peek();
195
+ if (!prev.has(id)) {
196
+ const next = new Set(prev);
197
+ next.add(id);
198
+ ids.set(next);
199
+ }
200
+ anchor = id;
201
+ };
202
+ const deselect = (id) => {
203
+ const prev = ids.peek();
204
+ if (!prev.has(id)) return;
205
+ const next = new Set(prev);
206
+ next.delete(id);
207
+ ids.set(next);
208
+ };
209
+ const toggle = (id) => {
210
+ const prev = ids.peek();
211
+ const next = new Set(prev);
212
+ if (prev.has(id)) next.delete(id);
213
+ else {
214
+ next.add(id);
215
+ anchor = id;
216
+ }
217
+ ids.set(next);
218
+ };
219
+ const clear = () => {
220
+ if (ids.peek().size === 0) {
221
+ anchor = null;
222
+ return;
223
+ }
224
+ ids.set(/* @__PURE__ */ new Set());
225
+ anchor = null;
226
+ };
227
+ const selectAll = (incoming) => {
228
+ ids.set(new Set(incoming));
229
+ anchor = incoming.length > 0 ? incoming[incoming.length - 1] ?? null : null;
230
+ };
231
+ const handleClick = (id, mods, ordered) => {
232
+ if (mods.shift && anchor !== null) {
233
+ const anchorIdx = ordered.indexOf(anchor);
234
+ const targetIdx = ordered.indexOf(id);
235
+ if (anchorIdx === -1 || targetIdx === -1) {
236
+ ids.set(new Set([id]));
237
+ anchor = id;
238
+ preShiftSelection = null;
239
+ return;
240
+ }
241
+ if (preShiftSelection === null) preShiftSelection = ids.peek();
242
+ const [lo, hi] = anchorIdx < targetIdx ? [anchorIdx, targetIdx] : [targetIdx, anchorIdx];
243
+ const next = new Set(preShiftSelection);
244
+ for (const k of ordered.slice(lo, hi + 1)) next.add(k);
245
+ ids.set(next);
246
+ return;
247
+ }
248
+ preShiftSelection = null;
249
+ if (mods.meta) {
250
+ toggle(id);
251
+ return;
252
+ }
253
+ ids.set(new Set([id]));
254
+ anchor = id;
255
+ };
256
+ return {
257
+ selectedIds: readOnly(ids),
258
+ size,
259
+ isSelected,
260
+ select,
261
+ deselect,
262
+ toggle,
263
+ clear,
264
+ selectAll,
265
+ handleClick
266
+ };
267
+ }
268
+ //#endregion
269
+ //#region src/timing/debounced.ts
270
+ /**
271
+ * Lag a signal by `ms`. The returned signal updates only after the source has
272
+ * been unchanged for `ms`. Each new write resets the timer.
273
+ *
274
+ * No lifecycle — the internal effect runs for the lifetime of the program.
275
+ * Use inside a controller closure so it dies with the closure.
276
+ */
277
+ function debounced(source, ms) {
278
+ const out = signal(source.peek());
279
+ let timer = null;
280
+ let initial = true;
281
+ effect(() => {
282
+ const value = source.value;
283
+ if (initial) {
284
+ initial = false;
285
+ return;
286
+ }
287
+ if (timer != null) clearTimeout(timer);
288
+ timer = setTimeout(() => {
289
+ out.set(value);
290
+ timer = null;
291
+ }, ms);
292
+ });
293
+ return out;
294
+ }
295
+ //#endregion
296
+ //#region src/timing/throttled.ts
297
+ /**
298
+ * Rate-limit a signal so it emits at most once per `ms` (leading + trailing).
299
+ * The first change passes through immediately. Subsequent changes within the
300
+ * window are coalesced; the latest value is emitted when the window expires.
301
+ *
302
+ * No lifecycle — see debounced() note.
303
+ */
304
+ function throttled(source, ms) {
305
+ const out = signal(source.peek());
306
+ let lastEmit = Number.NEGATIVE_INFINITY;
307
+ let trailingTimer = null;
308
+ let trailingValue = source.peek();
309
+ let initial = true;
310
+ effect(() => {
311
+ const value = source.value;
312
+ if (initial) {
313
+ initial = false;
314
+ return;
315
+ }
316
+ const now = Date.now();
317
+ const elapsed = now - lastEmit;
318
+ if (elapsed >= ms) {
319
+ out.set(value);
320
+ lastEmit = now;
321
+ if (trailingTimer != null) {
322
+ clearTimeout(trailingTimer);
323
+ trailingTimer = null;
324
+ }
325
+ } else {
326
+ trailingValue = value;
327
+ if (trailingTimer == null) trailingTimer = setTimeout(() => {
328
+ out.set(trailingValue);
329
+ lastEmit = Date.now();
330
+ trailingTimer = null;
331
+ }, ms - elapsed);
332
+ }
333
+ });
334
+ return out;
335
+ }
336
+ //#endregion
337
+ export { batch, computed, createEmitter, createRoot, debounced, debouncedValidator, defineController, defineInfiniteQuery, defineQuery, defineScope, effect, email, isAbortError, lookupRegisteredQuery, max, maxLength, min, minLength, pattern, required, selection, signal, throttled, untracked };
338
+
339
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/signals/readonly.ts","../src/forms/validators.ts","../src/query/define.ts","../src/scope.ts","../src/selection.ts","../src/timing/debounced.ts","../src/timing/throttled.ts"],"sourcesContent":["import type { ReadSignal } from './types'\n\n/**\n * Project a Signal (or any object with a reactive `value` + `peek` + `subscribe`)\n * as a `ReadSignal`. The returned object does not expose `set` / `update` /\n * settable `value`, so it can be returned from APIs without callers mutating it.\n *\n * Internal helper — not exported from the package's public surface.\n */\nexport function readOnly<T>(source: ReadSignal<T>): ReadSignal<T> {\n return {\n get value() {\n return source.value\n },\n peek() {\n return source.peek()\n },\n subscribe(handler) {\n return source.subscribe(handler)\n },\n }\n}\n","import type { Validator } from './types'\n\nconst isEmpty = (value: unknown): boolean => {\n if (value === undefined || value === null) return true\n if (typeof value === 'string') return value.length === 0\n if (Array.isArray(value)) return value.length === 0\n return false\n}\n\n/** Reject empty values (undefined, null, empty string, empty array). */\nexport const required =\n <T>(message = 'Required'): Validator<T> =>\n (value) =>\n isEmpty(value) ? message : null\n\n/** Reject strings / arrays shorter than `n`. Allows null/undefined (use with `required` to forbid). */\nexport const minLength =\n (n: number, message?: string): Validator<string | readonly unknown[]> =>\n (value) => {\n if (value == null) return null\n if (value.length >= n) return null\n return message ?? `Must be at least ${n} characters`\n }\n\n/** Reject strings / arrays longer than `n`. */\nexport const maxLength =\n (n: number, message?: string): Validator<string | readonly unknown[]> =>\n (value) => {\n if (value == null) return null\n if (value.length <= n) return null\n return message ?? `Must be no more than ${n} characters`\n }\n\n/** Reject numbers less than `n`. */\nexport const min =\n (n: number, message?: string): Validator<number> =>\n (value) => {\n if (value == null) return null\n if (value >= n) return null\n return message ?? `Must be at least ${n}`\n }\n\n/** Reject numbers greater than `n`. */\nexport const max =\n (n: number, message?: string): Validator<number> =>\n (value) => {\n if (value == null) return null\n if (value <= n) return null\n return message ?? `Must be no more than ${n}`\n }\n\n// RFC-5322-light. Pragmatic, not exhaustive — production forms should\n// rely on server-side validation for definitive answers.\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\n/** Reject strings that don't look like an email. Empty / null pass (use with `required` to forbid). */\nexport const email =\n (message = 'Invalid email address'): Validator<string> =>\n (value) => {\n if (value == null || value === '') return null\n return EMAIL_RE.test(value) ? null : message\n }\n\n/** Reject strings that don't match the supplied `RegExp`. */\nexport const pattern =\n (re: RegExp, message = 'Invalid format'): Validator<string> =>\n (value) => {\n if (value == null || value === '') return null\n return re.test(value) ? null : message\n }\n","import type { QueryClient } from './client'\nimport type { InfiniteQuery, InfiniteQuerySpec } from './infinite'\nimport { type RegisteredQuery, registerQueryById } from './plugin'\nimport type { Query, QuerySpec, Snapshot } from './types'\n\ntype QueryInternal<Args extends unknown[], T> = Query<Args, T> & {\n readonly __spec: QuerySpec<Args, T>\n __clients: Set<QueryClient>\n}\n\nconst warnedMissingId = new WeakSet<object>()\n\nfunction registerQueryId(spec: { queryId?: string; crossTab?: boolean }, query: object): void {\n if (spec.queryId != null) {\n registerQueryById(spec.queryId, query as RegisteredQuery)\n } else if (spec.crossTab === true) {\n // Plugins can't route a message without a `queryId`. Warn once per\n // offending spec — repeated warnings on every render would be noisy.\n if (__DEV__ && !warnedMissingId.has(spec as object)) {\n warnedMissingId.add(spec as object)\n console.warn(\n '[olas] defineQuery({ crossTab: true }) requires a stable `queryId`. ' +\n 'Add `queryId: \"<unique-string>\"` to the spec. Cross-tab sync is disabled for this query.',\n )\n }\n }\n}\n\n/**\n * Define a keyed, shared query. The returned Query value lives at module\n * scope; per-root QueryClients bind their own entry registries to it.\n */\nexport function defineQuery<Args extends unknown[], T>(spec: QuerySpec<Args, T>): Query<Args, T> {\n const clients = new Set<QueryClient>()\n const query = {\n __olas: 'query' as const,\n __spec: spec,\n __clients: clients,\n\n invalidate(...args: Args): void {\n for (const client of clients) {\n client.invalidate(query as Query<Args, T>, args)\n }\n },\n\n invalidateAll(): void {\n for (const client of clients) {\n client.invalidateAll(query as Query<Args, T>)\n }\n },\n\n setData(...rest: [...Args, updater: (prev: T | undefined) => T]): Snapshot {\n const updater = rest[rest.length - 1] as (prev: T | undefined) => T\n const keyArgs = rest.slice(0, -1) as unknown as Args\n const childSnapshots: Snapshot[] = []\n for (const client of clients) {\n childSnapshots.push(client.setData(query as Query<Args, T>, keyArgs, updater))\n }\n return {\n rollback: () => {\n for (const s of childSnapshots) s.rollback()\n },\n finalize: () => {\n for (const s of childSnapshots) s.finalize()\n },\n }\n },\n\n prefetch(...args: Args): Promise<T> {\n // Single-client common case; if none, throw.\n const [first] = clients\n if (!first) {\n return Promise.reject(new Error('[olas] prefetch called before any root has subscribed'))\n }\n return first.prefetch(query as Query<Args, T>, args)\n },\n } satisfies QueryInternal<Args, T>\n\n registerQueryId(spec, query)\n return query as Query<Args, T>\n}\n\ntype InfiniteQueryInternal<Args extends unknown[], TPage, TItem> = InfiniteQuery<\n Args,\n TPage,\n TItem\n> & {\n readonly __spec: InfiniteQuerySpec<Args, any, TPage, TItem>\n __clients: Set<QueryClient>\n}\n\n/**\n * Define a paginated query (chat-style \"load more\", infinite scrolling). Pages\n * are kept in order and concatenated via `getNextPageParam` /\n * `getPreviousPageParam`. The returned handle is module-scoped — bind\n * subscribers via `ctx.use(infiniteQuery, () => [...args])`. Spec §5.7,\n * §20.4.\n */\nexport function defineInfiniteQuery<Args extends unknown[], PageParam, TPage, TItem = TPage>(\n spec: InfiniteQuerySpec<Args, PageParam, TPage, TItem>,\n): InfiniteQuery<Args, TPage, TItem> {\n const clients = new Set<QueryClient>()\n const query = {\n __olas: 'infiniteQuery' as const,\n __spec: spec,\n __clients: clients,\n\n invalidate(...args: Args): void {\n for (const client of clients) {\n client.invalidateInfinite(query as InfiniteQuery<Args, TPage, TItem>, args)\n }\n },\n\n invalidateAll(): void {\n for (const client of clients) {\n client.invalidateAllInfinite(query as InfiniteQuery<Args, TPage, TItem>)\n }\n },\n\n setData(...rest: [...Args, updater: (prev: TPage[] | undefined) => TPage[]]): Snapshot {\n const updater = rest[rest.length - 1] as (prev: TPage[] | undefined) => TPage[]\n const keyArgs = rest.slice(0, -1) as unknown as Args\n const childSnapshots: Snapshot[] = []\n for (const client of clients) {\n childSnapshots.push(\n client.setInfiniteData<Args, TPage>(\n query as InfiniteQuery<Args, TPage, TItem>,\n keyArgs,\n updater,\n ),\n )\n }\n return {\n rollback: () => {\n for (const s of childSnapshots) s.rollback()\n },\n finalize: () => {\n for (const s of childSnapshots) s.finalize()\n },\n }\n },\n\n prefetch(...args: Args): Promise<TPage> {\n const [first] = clients\n if (!first) {\n return Promise.reject(new Error('[olas] prefetch called before any root has subscribed'))\n }\n return first.prefetchInfinite(query as InfiniteQuery<Args, TPage, TItem>, args)\n },\n } satisfies InfiniteQueryInternal<Args, TPage, TItem>\n\n registerQueryId(spec, query)\n return query as InfiniteQuery<Args, TPage, TItem>\n}\n","/**\n * Typed cross-tree data slot. Provided by an ancestor via `ctx.provide(scope, value)`\n * and consumed anywhere in its subtree via `ctx.inject(scope)`. Defined at module\n * scope so the identity is stable across calls. See spec §10.3.\n */\nexport type Scope<T> = {\n readonly __olas: 'scope'\n /** Per-scope identity; matches across `provide` / `inject`. */\n readonly __id: symbol\n /** Optional human-readable name (used in error messages). */\n readonly name?: string\n /** Default value used when no provider exists; `undefined` if none was set. */\n readonly default?: T\n /** True iff `defineScope` was called with a `default` (even `default: undefined`). */\n readonly hasDefault: boolean\n // Phantom for inference — typed `T` is preserved through the scope's lifetime.\n readonly __t?: T\n}\n\nexport type ScopeOptions<T> = {\n default?: T\n name?: string\n}\n\n/**\n * Create a scope. The returned value is the typed handle passed to\n * `ctx.provide(scope, value)` and `ctx.inject(scope)`. Identity is keyed by\n * an internal symbol so two `defineScope()` calls — even with identical\n * options — yield distinct scopes.\n */\nexport function defineScope<T>(options?: ScopeOptions<T>): Scope<T> {\n const hasDefault = options !== undefined && 'default' in options\n const name = options?.name\n const scope: Scope<T> = {\n __olas: 'scope',\n __id: Symbol(name ?? 'scope'),\n hasDefault,\n ...(name !== undefined ? { name } : {}),\n ...(hasDefault ? { default: options?.default as T } : {}),\n }\n return scope\n}\n","import { computed, signal } from './signals'\nimport { readOnly } from './signals/readonly'\nimport type { ReadSignal } from './signals/types'\n\n/**\n * Multi-select state for tables / lists with bulk actions (spec §17.5).\n *\n * Plain function — not bound to `ctx`. Place it in a controller's closure so\n * it dies with the closure. The phantom `T` parameter brands the selection by\n * item type; IDs are always strings.\n */\n// biome-ignore lint/correctness/noUnusedVariables: phantom branding param (spec §17.5)\nexport type Selection<T = unknown> = {\n selectedIds: ReadSignal<ReadonlySet<string>>\n size: ReadSignal<number>\n isSelected(id: string): ReadSignal<boolean>\n\n select(id: string): void\n deselect(id: string): void\n toggle(id: string): void\n clear(): void\n selectAll(ids: readonly string[]): void\n\n handleClick(\n id: string,\n mods: { shift?: boolean; meta?: boolean },\n ordered: readonly string[],\n ): void\n}\n\n/**\n * Create a `Selection<T>`. Optional `initial` seeds the selected set.\n *\n * `handleClick` encapsulates the standard click semantics:\n * - plain click → select only `id` (anchor moves to `id`)\n * - meta-click → toggle `id` (anchor moves to `id` on add)\n * - shift-click → range from anchor to `id` along `ordered` (anchor sticks,\n * so subsequent shift-clicks extend from the same origin)\n *\n * Spec §16.5 / §17.5.\n */\nexport function selection<T = unknown>(options?: { initial?: readonly string[] }): Selection<T> {\n const ids = signal<ReadonlySet<string>>(new Set(options?.initial))\n let anchor: string | null = options?.initial?.length\n ? (options.initial[options.initial.length - 1] ?? null)\n : null\n // Snapshot of the selection just before the first shift-click of a run.\n // Subsequent shift-clicks re-compute the range against this snapshot so the\n // user can shrink or grow the range. Reset on any non-shift click.\n let preShiftSelection: ReadonlySet<string> | null = null\n\n const size = computed(() => ids.value.size)\n\n const isSelected = (id: string): ReadSignal<boolean> => computed(() => ids.value.has(id))\n\n const select = (id: string): void => {\n const prev = ids.peek()\n if (!prev.has(id)) {\n const next = new Set(prev)\n next.add(id)\n ids.set(next)\n }\n anchor = id\n }\n\n const deselect = (id: string): void => {\n const prev = ids.peek()\n if (!prev.has(id)) return\n const next = new Set(prev)\n next.delete(id)\n ids.set(next)\n }\n\n const toggle = (id: string): void => {\n const prev = ids.peek()\n const next = new Set(prev)\n if (prev.has(id)) {\n next.delete(id)\n } else {\n next.add(id)\n anchor = id\n }\n ids.set(next)\n }\n\n const clear = (): void => {\n if (ids.peek().size === 0) {\n anchor = null\n return\n }\n ids.set(new Set())\n anchor = null\n }\n\n const selectAll = (incoming: readonly string[]): void => {\n ids.set(new Set(incoming))\n anchor = incoming.length > 0 ? (incoming[incoming.length - 1] ?? null) : null\n }\n\n const handleClick = (\n id: string,\n mods: { shift?: boolean; meta?: boolean },\n ordered: readonly string[],\n ): void => {\n if (mods.shift && anchor !== null) {\n const anchorIdx = ordered.indexOf(anchor)\n const targetIdx = ordered.indexOf(id)\n if (anchorIdx === -1 || targetIdx === -1) {\n // anchor or target not visible — fall back to plain select\n ids.set(new Set([id]))\n anchor = id\n preShiftSelection = null\n return\n }\n if (preShiftSelection === null) {\n preShiftSelection = ids.peek()\n }\n const [lo, hi] = anchorIdx < targetIdx ? [anchorIdx, targetIdx] : [targetIdx, anchorIdx]\n const next = new Set(preShiftSelection)\n for (const k of ordered.slice(lo, hi + 1)) next.add(k)\n ids.set(next)\n // Anchor stays — subsequent shift-clicks extend from the same origin.\n return\n }\n // Any non-shift click ends the shift run.\n preShiftSelection = null\n if (mods.meta) {\n toggle(id)\n return\n }\n ids.set(new Set([id]))\n anchor = id\n }\n\n return {\n selectedIds: readOnly(ids),\n size,\n isSelected,\n select,\n deselect,\n toggle,\n clear,\n selectAll,\n handleClick,\n }\n}\n","import { effect, signal } from '../signals'\nimport type { ReadSignal } from '../signals/types'\n\n/**\n * Lag a signal by `ms`. The returned signal updates only after the source has\n * been unchanged for `ms`. Each new write resets the timer.\n *\n * No lifecycle — the internal effect runs for the lifetime of the program.\n * Use inside a controller closure so it dies with the closure.\n */\nexport function debounced<T>(source: ReadSignal<T>, ms: number): ReadSignal<T> {\n const out = signal<T>(source.peek())\n let timer: ReturnType<typeof setTimeout> | null = null\n let initial = true\n\n effect(() => {\n const value = source.value\n if (initial) {\n // The first effect run reads the source for tracking; we already\n // initialized `out` to the same value, so skip scheduling.\n initial = false\n return\n }\n if (timer != null) clearTimeout(timer)\n timer = setTimeout(() => {\n out.set(value)\n timer = null\n }, ms)\n })\n\n return out\n}\n","import { effect, signal } from '../signals'\nimport type { ReadSignal } from '../signals/types'\n\n/**\n * Rate-limit a signal so it emits at most once per `ms` (leading + trailing).\n * The first change passes through immediately. Subsequent changes within the\n * window are coalesced; the latest value is emitted when the window expires.\n *\n * No lifecycle — see debounced() note.\n */\nexport function throttled<T>(source: ReadSignal<T>, ms: number): ReadSignal<T> {\n const out = signal<T>(source.peek())\n let lastEmit = Number.NEGATIVE_INFINITY\n let trailingTimer: ReturnType<typeof setTimeout> | null = null\n let trailingValue: T = source.peek()\n let initial = true\n\n effect(() => {\n const value = source.value\n if (initial) {\n initial = false\n return\n }\n const now = Date.now()\n const elapsed = now - lastEmit\n if (elapsed >= ms) {\n out.set(value)\n lastEmit = now\n if (trailingTimer != null) {\n clearTimeout(trailingTimer)\n trailingTimer = null\n }\n } else {\n trailingValue = value\n if (trailingTimer == null) {\n trailingTimer = setTimeout(() => {\n out.set(trailingValue)\n lastEmit = Date.now()\n trailingTimer = null\n }, ms - elapsed)\n }\n }\n })\n\n return out\n}\n"],"mappings":"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"}