@skygraph/core 0.5.3 → 0.6.6

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 (37) hide show
  1. package/README.md +48 -48
  2. package/dist/calendar.cjs.map +1 -1
  3. package/dist/calendar.js +1 -1
  4. package/dist/{chunk-7FA2TBSZ.js → chunk-3DDKJILC.js} +1 -1
  5. package/dist/chunk-3DDKJILC.js.map +1 -0
  6. package/dist/{chunk-GEMALROQ.js → chunk-75BQUWZC.js} +1 -1
  7. package/dist/chunk-75BQUWZC.js.map +1 -0
  8. package/dist/{chunk-ZWB7JGAJ.js → chunk-CEX4ZVZF.js} +1 -1
  9. package/dist/chunk-CEX4ZVZF.js.map +1 -0
  10. package/dist/{chunk-AB3RLBLW.js → chunk-MEZUJRC7.js} +1 -1
  11. package/dist/chunk-MEZUJRC7.js.map +1 -0
  12. package/dist/{chunk-Y7FTBBYX.js → chunk-QYUKEEBL.js} +1 -1
  13. package/dist/chunk-QYUKEEBL.js.map +1 -0
  14. package/dist/{chunk-IY6LJU3L.js → chunk-SUGXXNZK.js} +1 -1
  15. package/dist/chunk-SUGXXNZK.js.map +1 -0
  16. package/dist/form.cjs.map +1 -1
  17. package/dist/graph.cjs.map +1 -1
  18. package/dist/graph.js +1 -1
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.js +6 -6
  21. package/dist/index.js.map +1 -1
  22. package/dist/runtime-internal.cjs.map +1 -1
  23. package/dist/runtime-internal.js +1 -1
  24. package/dist/runtime-internal.js.map +1 -1
  25. package/dist/table.cjs.map +1 -1
  26. package/dist/table.js +1 -1
  27. package/dist/tree.cjs.map +1 -1
  28. package/dist/tree.js +1 -1
  29. package/dist/virtual.cjs.map +1 -1
  30. package/dist/virtual.js +1 -1
  31. package/package.json +1 -1
  32. package/dist/chunk-7FA2TBSZ.js.map +0 -1
  33. package/dist/chunk-AB3RLBLW.js.map +0 -1
  34. package/dist/chunk-GEMALROQ.js.map +0 -1
  35. package/dist/chunk-IY6LJU3L.js.map +0 -1
  36. package/dist/chunk-Y7FTBBYX.js.map +0 -1
  37. package/dist/chunk-ZWB7JGAJ.js.map +0 -1
package/dist/index.js CHANGED
@@ -7,14 +7,14 @@ import {
7
7
  matchesColumnFilter,
8
8
  matchesOperator,
9
9
  resolveOperator
10
- } from "./chunk-ZWB7JGAJ.js";
10
+ } from "./chunk-CEX4ZVZF.js";
11
11
  import {
12
12
  createTree
13
- } from "./chunk-Y7FTBBYX.js";
13
+ } from "./chunk-QYUKEEBL.js";
14
14
  import {
15
15
  createMeasureCache,
16
16
  createVirtual
17
- } from "./chunk-IY6LJU3L.js";
17
+ } from "./chunk-SUGXXNZK.js";
18
18
  import {
19
19
  aabbFromOBB,
20
20
  createGraph,
@@ -30,12 +30,12 @@ import {
30
30
  pointsToRoundedPath,
31
31
  resolveEdgeEndpoint,
32
32
  routeOrthogonal
33
- } from "./chunk-7FA2TBSZ.js";
33
+ } from "./chunk-3DDKJILC.js";
34
34
  import {
35
35
  createCalendar,
36
36
  detectConflicts,
37
37
  isAvailable
38
- } from "./chunk-AB3RLBLW.js";
38
+ } from "./chunk-MEZUJRC7.js";
39
39
  import {
40
40
  CALENDAR_PREFIX,
41
41
  FORM_META_PREFIX,
@@ -51,7 +51,7 @@ import {
51
51
  Store,
52
52
  SubscriptionEngine,
53
53
  Transaction
54
- } from "./chunk-GEMALROQ.js";
54
+ } from "./chunk-75BQUWZC.js";
55
55
 
56
56
  // src/Core.ts
57
57
  var CoreImpl = class {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Core.ts","../src/middleware/logger.ts","../src/middleware/freeze.ts","../src/middleware/persistence.ts","../src/middleware/validation.ts","../src/plugins/history.ts","../src/typed.ts"],"sourcesContent":["import { Store } from './runtime/Store'\nimport { SubscriptionEngine } from './runtime/SubscriptionEngine'\nimport { Batch } from './runtime/Batch'\nimport { Transaction } from './runtime/Transaction'\nimport { ComputedEngine } from './runtime/ComputedEngine'\nimport type { Core, Middleware, WriteEvent, NextFn } from './types'\n\nclass CoreImpl implements Core {\n private store = new Store()\n private subscriptions = new SubscriptionEngine()\n private batch_ = new Batch()\n private transaction_ = new Transaction()\n private computed_ = new ComputedEngine()\n\n private middlewares: Middleware[] = []\n\n // ── Middleware ──\n\n use(middleware: Middleware): () => void {\n this.middlewares.push(middleware)\n return () => {\n const idx = this.middlewares.indexOf(middleware)\n if (idx !== -1) this.middlewares.splice(idx, 1)\n }\n }\n\n private dispatchWrite(event: WriteEvent): void {\n let idx = 0\n const chain: NextFn = (ev: WriteEvent) => {\n if (idx < this.middlewares.length) {\n const mw = this.middlewares[idx++]\n mw(ev, chain)\n } else {\n this.commitWrite(ev)\n }\n }\n chain(event)\n }\n\n private commitWrite(event: WriteEvent): void {\n this.store.set(event.path, event.value)\n }\n\n // ── Public API ──\n\n get(path: string): unknown {\n return this.store.get(path)\n }\n\n set(path: string, value: unknown): void {\n if (this.transaction_.isActive) {\n this.transaction_.stage(path, value, this.store.get(path))\n return\n }\n\n const oldValue = this.store.get(path)\n if (Object.is(oldValue, value)) return\n\n this.dispatchWrite({ path, value, oldValue, source: 'user' })\n\n const computedChanges = this.propagateComputed(new Set([path]))\n\n if (this.batch_.isActive) {\n this.batch_.enqueue(path, value)\n for (const [cp, cv] of computedChanges) {\n this.batch_.enqueue(cp, cv)\n }\n return\n }\n\n this.subscriptions.notify(path, value)\n this.subscriptions.notifyMany(computedChanges)\n }\n\n subscribe(path: string, cb: (value: unknown) => void): () => void {\n return this.subscriptions.subscribe(path, cb)\n }\n\n batch(fn: () => void): void {\n this.batch_.start()\n try {\n fn()\n } finally {\n const changes = this.batch_.end()\n if (changes) {\n this.subscriptions.notifyMany(changes)\n }\n }\n }\n\n transaction(fn: () => void): void {\n this.batch_.start()\n this.transaction_.start()\n try {\n fn()\n const staged = this.transaction_.getStaged()\n\n // Pass all writes through middleware first, collecting approved ones.\n // dispatchWrite normally commits to store, so we temporarily redirect\n // commits to a pending list and only apply them atomically if all succeed.\n const approved: Array<{ path: string; value: unknown }> = []\n const origCommit = this.commitWrite.bind(this)\n this.commitWrite = (ev: WriteEvent) => {\n approved.push({ path: ev.path, value: ev.value })\n }\n\n try {\n for (const write of staged) {\n if (Object.is(write.value, write.prevValue)) continue\n this.dispatchWrite({\n path: write.path,\n value: write.value,\n oldValue: write.prevValue,\n source: 'transaction',\n })\n }\n } finally {\n this.commitWrite = origCommit\n }\n\n // All middleware passed — atomically apply to store\n const changes = new Map<string, unknown>()\n for (const { path, value } of approved) {\n this.store.set(path, value)\n changes.set(path, value)\n }\n\n this.transaction_.complete()\n\n const computedChanges = this.propagateComputed(new Set(changes.keys()))\n\n for (const [path, value] of changes) {\n this.batch_.enqueue(path, value)\n }\n for (const [path, value] of computedChanges) {\n this.batch_.enqueue(path, value)\n }\n } catch (e) {\n this.transaction_.rollback()\n throw e\n } finally {\n const batchedChanges = this.batch_.end()\n if (batchedChanges) {\n this.subscriptions.notifyMany(batchedChanges)\n }\n }\n }\n\n computed(\n target: string,\n deps: string[],\n fn: (...values: unknown[]) => unknown,\n ): void {\n this.computed_.register(target, deps, fn)\n const depValues = deps.map((d) => this.store.get(d))\n const value = fn(...depValues)\n const oldValue = this.store.get(target)\n this.dispatchWrite({\n path: target,\n value,\n oldValue,\n source: 'computed',\n })\n\n if (!Object.is(oldValue, value)) {\n this.subscriptions.notify(target, value)\n }\n }\n\n // ── Snapshot / Restore ──\n\n snapshot(): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n for (const path of this.store.paths()) {\n result[path] = this.store.get(path)\n }\n return result\n }\n\n restore(snap: Record<string, unknown>): void {\n this.batch_.start()\n try {\n for (const [path, value] of Object.entries(snap)) {\n const oldValue = this.store.get(path)\n if (!Object.is(oldValue, value)) {\n this.dispatchWrite({ path, value, oldValue, source: 'restore' })\n this.batch_.enqueue(path, value)\n }\n }\n } finally {\n const changes = this.batch_.end()\n if (changes) {\n this.subscriptions.notifyMany(changes)\n }\n }\n }\n\n // ── Internal helpers ──\n\n private propagateComputed(changedPaths: Set<string>): Map<string, unknown> {\n const rawChanges = this.computed_.propagateRaw(changedPaths, this.store)\n const dispatched = new Map<string, unknown>()\n\n for (const [path, { value, oldValue }] of rawChanges) {\n this.dispatchWrite({ path, value, oldValue, source: 'computed' })\n dispatched.set(path, value)\n }\n\n return dispatched\n }\n}\n\nexport function createCore(): Core {\n return new CoreImpl()\n}\n","import type { Middleware, WriteEvent, NextFn } from '../types'\n\nexport interface LoggerOptions {\n filter?: (event: WriteEvent) => boolean\n log?: (message: string, event: WriteEvent) => void\n}\n\nexport function loggerMiddleware(options: LoggerOptions = {}): Middleware {\n const log = options.log ?? ((msg, ev) => console.log(msg, ev.path, ev.value))\n const filter = options.filter\n\n return (event: WriteEvent, next: NextFn) => {\n if (filter && !filter(event)) {\n next(event)\n return\n }\n log(`[skygraph:${event.source}]`, event)\n next(event)\n }\n}\n","import type { Middleware, WriteEvent, NextFn } from '../types'\n\n/**\n * Prevents writes to specified path prefixes.\n * Useful for making parts of the store read-only.\n */\nexport function freezeMiddleware(frozenPaths: string[]): Middleware {\n return (event: WriteEvent, next: NextFn) => {\n for (const prefix of frozenPaths) {\n if (event.path === prefix || event.path.startsWith(prefix + '.')) {\n return\n }\n }\n next(event)\n }\n}\n","import type { Middleware, WriteEvent, NextFn } from '../types'\n\nexport interface PersistenceOptions {\n key?: string\n paths?: string[]\n storage?: {\n getItem(key: string): string | null\n setItem(key: string, value: string): void\n }\n debounce?: number\n}\n\n/**\n * Persists selected paths to a storage backend (localStorage by default).\n * Debounced to avoid excessive writes.\n */\nexport function persistenceMiddleware(options: PersistenceOptions = {}): Middleware {\n const storageKey = options.key ?? 'skygraph:state'\n const storage = options.storage ?? (typeof localStorage !== 'undefined' ? localStorage : null)\n const paths = options.paths\n const debounceMs = options.debounce ?? 100\n\n let pending: Record<string, unknown> = {}\n let timer: ReturnType<typeof setTimeout> | null = null\n\n function flush() {\n if (!storage) return\n const existing = storage.getItem(storageKey)\n let data: Record<string, unknown> = {}\n if (existing) {\n try {\n data = JSON.parse(existing)\n } catch {\n /* corrupted, start fresh */\n }\n }\n Object.assign(data, pending)\n storage.setItem(storageKey, JSON.stringify(data))\n pending = {}\n timer = null\n }\n\n return (event: WriteEvent, next: NextFn) => {\n next(event)\n\n if (paths && !paths.some((p) => event.path === p || event.path.startsWith(p + '.'))) {\n return\n }\n\n pending[event.path] = event.value\n\n if (timer === null) {\n timer = setTimeout(flush, debounceMs)\n }\n }\n}\n","import type { Middleware, WriteEvent, NextFn } from '../types'\n\nexport type Validator = (event: WriteEvent) => boolean | string\n\nexport interface ValidationMiddlewareOptions {\n validators: Record<string, Validator>\n onReject?: (event: WriteEvent, reason: string) => void\n}\n\n/**\n * Validates writes against a map of path -> validator.\n * If the validator returns false or a string (error message),\n * the write is rejected and onReject is called (if provided).\n */\nexport function validationMiddleware(\n optionsOrValidators: ValidationMiddlewareOptions | Record<string, Validator>,\n): Middleware {\n const isOptions = 'validators' in optionsOrValidators\n const validators: Record<string, Validator> = isOptions\n ? (optionsOrValidators as ValidationMiddlewareOptions).validators\n : (optionsOrValidators as Record<string, Validator>)\n const onReject = isOptions\n ? (optionsOrValidators as ValidationMiddlewareOptions).onReject\n : undefined\n\n return (event: WriteEvent, next: NextFn) => {\n const validator = validators[event.path]\n if (validator) {\n const result = validator(event)\n if (result === false || typeof result === 'string') {\n onReject?.(event, typeof result === 'string' ? result : 'Validation failed')\n return\n }\n }\n next(event)\n }\n}\n","import type { Core, Middleware, WriteEvent, NextFn } from '../types'\n\nexport interface HistoryEntry {\n timestamp: number\n patches: ReadonlyArray<{ path: string; value: unknown; oldValue: unknown }>\n}\n\nexport interface HistoryPlugin {\n readonly entries: ReadonlyArray<HistoryEntry>\n readonly cursor: number\n readonly canUndo: boolean\n readonly canRedo: boolean\n\n undo(): void\n redo(): void\n jumpTo(index: number): void\n clear(): void\n destroy(): void\n}\n\nexport interface HistoryOptions {\n maxEntries?: number\n /** Group multiple writes within this window (ms) into a single entry */\n groupWindow?: number\n filter?: (event: WriteEvent) => boolean\n}\n\nexport function createHistory(core: Core, options: HistoryOptions = {}): HistoryPlugin {\n const maxEntries = options.maxEntries ?? 100\n const groupWindow = options.groupWindow ?? 0\n const filter = options.filter\n\n const entries: HistoryEntry[] = []\n let cursor = -1\n let travelling = false\n let groupTimer: ReturnType<typeof setTimeout> | null = null\n let pendingPatches: HistoryEntry['patches'][number][] = []\n\n function flushGroup() {\n if (pendingPatches.length === 0) return\n const entry: HistoryEntry = {\n timestamp: Date.now(),\n patches: [...pendingPatches],\n }\n pendingPatches = []\n\n if (cursor < entries.length - 1) {\n entries.splice(cursor + 1)\n }\n\n entries.push(entry)\n cursor++\n if (entries.length > maxEntries) {\n entries.shift()\n cursor--\n }\n\n groupTimer = null\n }\n\n const middleware: Middleware = (event: WriteEvent, next: NextFn) => {\n next(event)\n\n if (travelling) return\n if (event.source === 'computed') return\n if (filter && !filter(event)) return\n\n pendingPatches.push({\n path: event.path,\n value: event.value,\n oldValue: event.oldValue,\n })\n\n if (groupWindow > 0) {\n if (groupTimer === null) {\n groupTimer = setTimeout(flushGroup, groupWindow)\n }\n } else {\n flushGroup()\n }\n }\n\n const unsub = core.use(middleware)\n\n function applyPatches(patches: HistoryEntry['patches'], direction: 'undo' | 'redo') {\n travelling = true\n try {\n core.batch(() => {\n if (direction === 'undo') {\n for (let i = patches.length - 1; i >= 0; i--) {\n core.set(patches[i].path, patches[i].oldValue)\n }\n } else {\n for (const patch of patches) {\n core.set(patch.path, patch.value)\n }\n }\n })\n } finally {\n travelling = false\n }\n }\n\n return {\n get entries() {\n return entries as ReadonlyArray<HistoryEntry>\n },\n get cursor() {\n return cursor\n },\n get canUndo() {\n return cursor >= 0\n },\n get canRedo() {\n return cursor < entries.length - 1\n },\n\n undo() {\n if (cursor < 0) return\n flushGroup()\n applyPatches(entries[cursor].patches, 'undo')\n cursor--\n },\n\n redo() {\n if (cursor >= entries.length - 1) return\n flushGroup()\n cursor++\n applyPatches(entries[cursor].patches, 'redo')\n },\n\n jumpTo(index: number) {\n if (index < -1 || index >= entries.length) return\n flushGroup()\n\n while (cursor > index) {\n applyPatches(entries[cursor].patches, 'undo')\n cursor--\n }\n while (cursor < index) {\n cursor++\n applyPatches(entries[cursor].patches, 'redo')\n }\n },\n\n clear() {\n entries.length = 0\n cursor = -1\n pendingPatches = []\n if (groupTimer !== null) {\n clearTimeout(groupTimer)\n groupTimer = null\n }\n },\n\n destroy() {\n this.clear()\n unsub()\n },\n }\n}\n","import type { Core } from './types'\n\n/**\n * Utility types for path-based type inference.\n * These provide compile-time type safety over the untyped Core runtime.\n */\n\ntype Primitive = string | number | boolean | null | undefined\n\n/**\n * Extracts dot-notation paths from a nested object type (max 3 levels deep).\n * Example: Path<{user: {name: string}}> = \"user\" | \"user.name\"\n */\nexport type Path<T> = T extends Primitive\n ? never\n : {\n [K in keyof T & string]: T[K] extends Primitive\n ? K\n : T[K] extends Record<string, unknown>\n ?\n | K\n | `${K}.${keyof T[K] & string}`\n | {\n [L in keyof T[K] & string]: T[K][L] extends Record<\n string,\n unknown\n >\n ? `${K}.${L}.${keyof T[K][L] & string}`\n : never\n }[keyof T[K] & string]\n : K\n }[keyof T & string]\n\n/**\n * Resolves the value type at a given dot-notation path (max 3 levels).\n */\nexport type PathValue<T, P extends string> = P extends keyof T\n ? T[P]\n : P extends `${infer K}.${infer Rest}`\n ? K extends keyof T\n ? Rest extends keyof T[K]\n ? T[K][Rest]\n : Rest extends `${infer L}.${infer M}`\n ? L extends keyof T[K]\n ? M extends keyof T[K][L]\n ? T[K][L][M]\n : unknown\n : unknown\n : unknown\n : unknown\n : unknown\n\n/**\n * Type-safe wrapper over Core.\n * Compile-time only — runtime behavior is identical to Core.\n */\nexport interface TypedCore<T> {\n get<K extends Path<T>>(path: K): PathValue<T, K>\n set<K extends Path<T>>(path: K, value: PathValue<T, K>): void\n subscribe<K extends Path<T>>(\n path: K,\n cb: (value: PathValue<T, K>) => void\n ): () => void\n batch(fn: () => void): void\n transaction(fn: () => void): void\n computed<K extends Path<T>>(\n target: K,\n deps: Path<T>[],\n fn: (...values: unknown[]) => PathValue<T, K>\n ): void\n /** Access the untyped core underneath */\n raw: Core\n}\n\n/**\n * Creates a type-safe wrapper around an existing Core instance.\n * No runtime overhead — just type narrowing.\n */\nexport function createTypedCore<T>(core: Core): TypedCore<T> {\n return {\n get: core.get.bind(core) as TypedCore<T>['get'],\n set: core.set.bind(core) as TypedCore<T>['set'],\n subscribe: core.subscribe.bind(core) as TypedCore<T>['subscribe'],\n batch: core.batch.bind(core),\n transaction: core.transaction.bind(core),\n computed: core.computed.bind(core) as TypedCore<T>['computed'],\n raw: core,\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/Core.ts","../src/middleware/logger.ts","../src/middleware/freeze.ts","../src/middleware/persistence.ts","../src/middleware/validation.ts","../src/plugins/history.ts","../src/typed.ts"],"sourcesContent":["import { Store } from './runtime/Store'\r\nimport { SubscriptionEngine } from './runtime/SubscriptionEngine'\r\nimport { Batch } from './runtime/Batch'\r\nimport { Transaction } from './runtime/Transaction'\r\nimport { ComputedEngine } from './runtime/ComputedEngine'\r\nimport type { Core, Middleware, WriteEvent, NextFn } from './types'\r\n\r\nclass CoreImpl implements Core {\r\n private store = new Store()\r\n private subscriptions = new SubscriptionEngine()\r\n private batch_ = new Batch()\r\n private transaction_ = new Transaction()\r\n private computed_ = new ComputedEngine()\r\n\r\n private middlewares: Middleware[] = []\r\n\r\n // ── Middleware ──\r\n\r\n use(middleware: Middleware): () => void {\r\n this.middlewares.push(middleware)\r\n return () => {\r\n const idx = this.middlewares.indexOf(middleware)\r\n if (idx !== -1) this.middlewares.splice(idx, 1)\r\n }\r\n }\r\n\r\n private dispatchWrite(event: WriteEvent): void {\r\n let idx = 0\r\n const chain: NextFn = (ev: WriteEvent) => {\r\n if (idx < this.middlewares.length) {\r\n const mw = this.middlewares[idx++]\r\n mw(ev, chain)\r\n } else {\r\n this.commitWrite(ev)\r\n }\r\n }\r\n chain(event)\r\n }\r\n\r\n private commitWrite(event: WriteEvent): void {\r\n this.store.set(event.path, event.value)\r\n }\r\n\r\n // ── Public API ──\r\n\r\n get(path: string): unknown {\r\n return this.store.get(path)\r\n }\r\n\r\n set(path: string, value: unknown): void {\r\n if (this.transaction_.isActive) {\r\n this.transaction_.stage(path, value, this.store.get(path))\r\n return\r\n }\r\n\r\n const oldValue = this.store.get(path)\r\n if (Object.is(oldValue, value)) return\r\n\r\n this.dispatchWrite({ path, value, oldValue, source: 'user' })\r\n\r\n const computedChanges = this.propagateComputed(new Set([path]))\r\n\r\n if (this.batch_.isActive) {\r\n this.batch_.enqueue(path, value)\r\n for (const [cp, cv] of computedChanges) {\r\n this.batch_.enqueue(cp, cv)\r\n }\r\n return\r\n }\r\n\r\n this.subscriptions.notify(path, value)\r\n this.subscriptions.notifyMany(computedChanges)\r\n }\r\n\r\n subscribe(path: string, cb: (value: unknown) => void): () => void {\r\n return this.subscriptions.subscribe(path, cb)\r\n }\r\n\r\n batch(fn: () => void): void {\r\n this.batch_.start()\r\n try {\r\n fn()\r\n } finally {\r\n const changes = this.batch_.end()\r\n if (changes) {\r\n this.subscriptions.notifyMany(changes)\r\n }\r\n }\r\n }\r\n\r\n transaction(fn: () => void): void {\r\n this.batch_.start()\r\n this.transaction_.start()\r\n try {\r\n fn()\r\n const staged = this.transaction_.getStaged()\r\n\r\n // Pass all writes through middleware first, collecting approved ones.\r\n // dispatchWrite normally commits to store, so we temporarily redirect\r\n // commits to a pending list and only apply them atomically if all succeed.\r\n const approved: Array<{ path: string; value: unknown }> = []\r\n const origCommit = this.commitWrite.bind(this)\r\n this.commitWrite = (ev: WriteEvent) => {\r\n approved.push({ path: ev.path, value: ev.value })\r\n }\r\n\r\n try {\r\n for (const write of staged) {\r\n if (Object.is(write.value, write.prevValue)) continue\r\n this.dispatchWrite({\r\n path: write.path,\r\n value: write.value,\r\n oldValue: write.prevValue,\r\n source: 'transaction',\r\n })\r\n }\r\n } finally {\r\n this.commitWrite = origCommit\r\n }\r\n\r\n // All middleware passed — atomically apply to store\r\n const changes = new Map<string, unknown>()\r\n for (const { path, value } of approved) {\r\n this.store.set(path, value)\r\n changes.set(path, value)\r\n }\r\n\r\n this.transaction_.complete()\r\n\r\n const computedChanges = this.propagateComputed(new Set(changes.keys()))\r\n\r\n for (const [path, value] of changes) {\r\n this.batch_.enqueue(path, value)\r\n }\r\n for (const [path, value] of computedChanges) {\r\n this.batch_.enqueue(path, value)\r\n }\r\n } catch (e) {\r\n this.transaction_.rollback()\r\n throw e\r\n } finally {\r\n const batchedChanges = this.batch_.end()\r\n if (batchedChanges) {\r\n this.subscriptions.notifyMany(batchedChanges)\r\n }\r\n }\r\n }\r\n\r\n computed(\r\n target: string,\r\n deps: string[],\r\n fn: (...values: unknown[]) => unknown,\r\n ): void {\r\n this.computed_.register(target, deps, fn)\r\n const depValues = deps.map((d) => this.store.get(d))\r\n const value = fn(...depValues)\r\n const oldValue = this.store.get(target)\r\n this.dispatchWrite({\r\n path: target,\r\n value,\r\n oldValue,\r\n source: 'computed',\r\n })\r\n\r\n if (!Object.is(oldValue, value)) {\r\n this.subscriptions.notify(target, value)\r\n }\r\n }\r\n\r\n // ── Snapshot / Restore ──\r\n\r\n snapshot(): Record<string, unknown> {\r\n const result: Record<string, unknown> = {}\r\n for (const path of this.store.paths()) {\r\n result[path] = this.store.get(path)\r\n }\r\n return result\r\n }\r\n\r\n restore(snap: Record<string, unknown>): void {\r\n this.batch_.start()\r\n try {\r\n for (const [path, value] of Object.entries(snap)) {\r\n const oldValue = this.store.get(path)\r\n if (!Object.is(oldValue, value)) {\r\n this.dispatchWrite({ path, value, oldValue, source: 'restore' })\r\n this.batch_.enqueue(path, value)\r\n }\r\n }\r\n } finally {\r\n const changes = this.batch_.end()\r\n if (changes) {\r\n this.subscriptions.notifyMany(changes)\r\n }\r\n }\r\n }\r\n\r\n // ── Internal helpers ──\r\n\r\n private propagateComputed(changedPaths: Set<string>): Map<string, unknown> {\r\n const rawChanges = this.computed_.propagateRaw(changedPaths, this.store)\r\n const dispatched = new Map<string, unknown>()\r\n\r\n for (const [path, { value, oldValue }] of rawChanges) {\r\n this.dispatchWrite({ path, value, oldValue, source: 'computed' })\r\n dispatched.set(path, value)\r\n }\r\n\r\n return dispatched\r\n }\r\n}\r\n\r\nexport function createCore(): Core {\r\n return new CoreImpl()\r\n}\r\n","import type { Middleware, WriteEvent, NextFn } from '../types'\r\n\r\nexport interface LoggerOptions {\r\n filter?: (event: WriteEvent) => boolean\r\n log?: (message: string, event: WriteEvent) => void\r\n}\r\n\r\nexport function loggerMiddleware(options: LoggerOptions = {}): Middleware {\r\n const log = options.log ?? ((msg, ev) => console.log(msg, ev.path, ev.value))\r\n const filter = options.filter\r\n\r\n return (event: WriteEvent, next: NextFn) => {\r\n if (filter && !filter(event)) {\r\n next(event)\r\n return\r\n }\r\n log(`[skygraph:${event.source}]`, event)\r\n next(event)\r\n }\r\n}\r\n","import type { Middleware, WriteEvent, NextFn } from '../types'\r\n\r\n/**\r\n * Prevents writes to specified path prefixes.\r\n * Useful for making parts of the store read-only.\r\n */\r\nexport function freezeMiddleware(frozenPaths: string[]): Middleware {\r\n return (event: WriteEvent, next: NextFn) => {\r\n for (const prefix of frozenPaths) {\r\n if (event.path === prefix || event.path.startsWith(prefix + '.')) {\r\n return\r\n }\r\n }\r\n next(event)\r\n }\r\n}\r\n","import type { Middleware, WriteEvent, NextFn } from '../types'\r\n\r\nexport interface PersistenceOptions {\r\n key?: string\r\n paths?: string[]\r\n storage?: {\r\n getItem(key: string): string | null\r\n setItem(key: string, value: string): void\r\n }\r\n debounce?: number\r\n}\r\n\r\n/**\r\n * Persists selected paths to a storage backend (localStorage by default).\r\n * Debounced to avoid excessive writes.\r\n */\r\nexport function persistenceMiddleware(options: PersistenceOptions = {}): Middleware {\r\n const storageKey = options.key ?? 'skygraph:state'\r\n const storage = options.storage ?? (typeof localStorage !== 'undefined' ? localStorage : null)\r\n const paths = options.paths\r\n const debounceMs = options.debounce ?? 100\r\n\r\n let pending: Record<string, unknown> = {}\r\n let timer: ReturnType<typeof setTimeout> | null = null\r\n\r\n function flush() {\r\n if (!storage) return\r\n const existing = storage.getItem(storageKey)\r\n let data: Record<string, unknown> = {}\r\n if (existing) {\r\n try {\r\n data = JSON.parse(existing)\r\n } catch {\r\n /* corrupted, start fresh */\r\n }\r\n }\r\n Object.assign(data, pending)\r\n storage.setItem(storageKey, JSON.stringify(data))\r\n pending = {}\r\n timer = null\r\n }\r\n\r\n return (event: WriteEvent, next: NextFn) => {\r\n next(event)\r\n\r\n if (paths && !paths.some((p) => event.path === p || event.path.startsWith(p + '.'))) {\r\n return\r\n }\r\n\r\n pending[event.path] = event.value\r\n\r\n if (timer === null) {\r\n timer = setTimeout(flush, debounceMs)\r\n }\r\n }\r\n}\r\n","import type { Middleware, WriteEvent, NextFn } from '../types'\r\n\r\nexport type Validator = (event: WriteEvent) => boolean | string\r\n\r\nexport interface ValidationMiddlewareOptions {\r\n validators: Record<string, Validator>\r\n onReject?: (event: WriteEvent, reason: string) => void\r\n}\r\n\r\n/**\r\n * Validates writes against a map of path -> validator.\r\n * If the validator returns false or a string (error message),\r\n * the write is rejected and onReject is called (if provided).\r\n */\r\nexport function validationMiddleware(\r\n optionsOrValidators: ValidationMiddlewareOptions | Record<string, Validator>,\r\n): Middleware {\r\n const isOptions = 'validators' in optionsOrValidators\r\n const validators: Record<string, Validator> = isOptions\r\n ? (optionsOrValidators as ValidationMiddlewareOptions).validators\r\n : (optionsOrValidators as Record<string, Validator>)\r\n const onReject = isOptions\r\n ? (optionsOrValidators as ValidationMiddlewareOptions).onReject\r\n : undefined\r\n\r\n return (event: WriteEvent, next: NextFn) => {\r\n const validator = validators[event.path]\r\n if (validator) {\r\n const result = validator(event)\r\n if (result === false || typeof result === 'string') {\r\n onReject?.(event, typeof result === 'string' ? result : 'Validation failed')\r\n return\r\n }\r\n }\r\n next(event)\r\n }\r\n}\r\n","import type { Core, Middleware, WriteEvent, NextFn } from '../types'\r\n\r\nexport interface HistoryEntry {\r\n timestamp: number\r\n patches: ReadonlyArray<{ path: string; value: unknown; oldValue: unknown }>\r\n}\r\n\r\nexport interface HistoryPlugin {\r\n readonly entries: ReadonlyArray<HistoryEntry>\r\n readonly cursor: number\r\n readonly canUndo: boolean\r\n readonly canRedo: boolean\r\n\r\n undo(): void\r\n redo(): void\r\n jumpTo(index: number): void\r\n clear(): void\r\n destroy(): void\r\n}\r\n\r\nexport interface HistoryOptions {\r\n maxEntries?: number\r\n /** Group multiple writes within this window (ms) into a single entry */\r\n groupWindow?: number\r\n filter?: (event: WriteEvent) => boolean\r\n}\r\n\r\nexport function createHistory(core: Core, options: HistoryOptions = {}): HistoryPlugin {\r\n const maxEntries = options.maxEntries ?? 100\r\n const groupWindow = options.groupWindow ?? 0\r\n const filter = options.filter\r\n\r\n const entries: HistoryEntry[] = []\r\n let cursor = -1\r\n let travelling = false\r\n let groupTimer: ReturnType<typeof setTimeout> | null = null\r\n let pendingPatches: HistoryEntry['patches'][number][] = []\r\n\r\n function flushGroup() {\r\n if (pendingPatches.length === 0) return\r\n const entry: HistoryEntry = {\r\n timestamp: Date.now(),\r\n patches: [...pendingPatches],\r\n }\r\n pendingPatches = []\r\n\r\n if (cursor < entries.length - 1) {\r\n entries.splice(cursor + 1)\r\n }\r\n\r\n entries.push(entry)\r\n cursor++\r\n if (entries.length > maxEntries) {\r\n entries.shift()\r\n cursor--\r\n }\r\n\r\n groupTimer = null\r\n }\r\n\r\n const middleware: Middleware = (event: WriteEvent, next: NextFn) => {\r\n next(event)\r\n\r\n if (travelling) return\r\n if (event.source === 'computed') return\r\n if (filter && !filter(event)) return\r\n\r\n pendingPatches.push({\r\n path: event.path,\r\n value: event.value,\r\n oldValue: event.oldValue,\r\n })\r\n\r\n if (groupWindow > 0) {\r\n if (groupTimer === null) {\r\n groupTimer = setTimeout(flushGroup, groupWindow)\r\n }\r\n } else {\r\n flushGroup()\r\n }\r\n }\r\n\r\n const unsub = core.use(middleware)\r\n\r\n function applyPatches(patches: HistoryEntry['patches'], direction: 'undo' | 'redo') {\r\n travelling = true\r\n try {\r\n core.batch(() => {\r\n if (direction === 'undo') {\r\n for (let i = patches.length - 1; i >= 0; i--) {\r\n core.set(patches[i].path, patches[i].oldValue)\r\n }\r\n } else {\r\n for (const patch of patches) {\r\n core.set(patch.path, patch.value)\r\n }\r\n }\r\n })\r\n } finally {\r\n travelling = false\r\n }\r\n }\r\n\r\n return {\r\n get entries() {\r\n return entries as ReadonlyArray<HistoryEntry>\r\n },\r\n get cursor() {\r\n return cursor\r\n },\r\n get canUndo() {\r\n return cursor >= 0\r\n },\r\n get canRedo() {\r\n return cursor < entries.length - 1\r\n },\r\n\r\n undo() {\r\n if (cursor < 0) return\r\n flushGroup()\r\n applyPatches(entries[cursor].patches, 'undo')\r\n cursor--\r\n },\r\n\r\n redo() {\r\n if (cursor >= entries.length - 1) return\r\n flushGroup()\r\n cursor++\r\n applyPatches(entries[cursor].patches, 'redo')\r\n },\r\n\r\n jumpTo(index: number) {\r\n if (index < -1 || index >= entries.length) return\r\n flushGroup()\r\n\r\n while (cursor > index) {\r\n applyPatches(entries[cursor].patches, 'undo')\r\n cursor--\r\n }\r\n while (cursor < index) {\r\n cursor++\r\n applyPatches(entries[cursor].patches, 'redo')\r\n }\r\n },\r\n\r\n clear() {\r\n entries.length = 0\r\n cursor = -1\r\n pendingPatches = []\r\n if (groupTimer !== null) {\r\n clearTimeout(groupTimer)\r\n groupTimer = null\r\n }\r\n },\r\n\r\n destroy() {\r\n this.clear()\r\n unsub()\r\n },\r\n }\r\n}\r\n","import type { Core } from './types'\r\n\r\n/**\r\n * Utility types for path-based type inference.\r\n * These provide compile-time type safety over the untyped Core runtime.\r\n */\r\n\r\ntype Primitive = string | number | boolean | null | undefined\r\n\r\n/**\r\n * Extracts dot-notation paths from a nested object type (max 3 levels deep).\r\n * Example: Path<{user: {name: string}}> = \"user\" | \"user.name\"\r\n */\r\nexport type Path<T> = T extends Primitive\r\n ? never\r\n : {\r\n [K in keyof T & string]: T[K] extends Primitive\r\n ? K\r\n : T[K] extends Record<string, unknown>\r\n ?\r\n | K\r\n | `${K}.${keyof T[K] & string}`\r\n | {\r\n [L in keyof T[K] & string]: T[K][L] extends Record<\r\n string,\r\n unknown\r\n >\r\n ? `${K}.${L}.${keyof T[K][L] & string}`\r\n : never\r\n }[keyof T[K] & string]\r\n : K\r\n }[keyof T & string]\r\n\r\n/**\r\n * Resolves the value type at a given dot-notation path (max 3 levels).\r\n */\r\nexport type PathValue<T, P extends string> = P extends keyof T\r\n ? T[P]\r\n : P extends `${infer K}.${infer Rest}`\r\n ? K extends keyof T\r\n ? Rest extends keyof T[K]\r\n ? T[K][Rest]\r\n : Rest extends `${infer L}.${infer M}`\r\n ? L extends keyof T[K]\r\n ? M extends keyof T[K][L]\r\n ? T[K][L][M]\r\n : unknown\r\n : unknown\r\n : unknown\r\n : unknown\r\n : unknown\r\n\r\n/**\r\n * Type-safe wrapper over Core.\r\n * Compile-time only — runtime behavior is identical to Core.\r\n */\r\nexport interface TypedCore<T> {\r\n get<K extends Path<T>>(path: K): PathValue<T, K>\r\n set<K extends Path<T>>(path: K, value: PathValue<T, K>): void\r\n subscribe<K extends Path<T>>(\r\n path: K,\r\n cb: (value: PathValue<T, K>) => void\r\n ): () => void\r\n batch(fn: () => void): void\r\n transaction(fn: () => void): void\r\n computed<K extends Path<T>>(\r\n target: K,\r\n deps: Path<T>[],\r\n fn: (...values: unknown[]) => PathValue<T, K>\r\n ): void\r\n /** Access the untyped core underneath */\r\n raw: Core\r\n}\r\n\r\n/**\r\n * Creates a type-safe wrapper around an existing Core instance.\r\n * No runtime overhead — just type narrowing.\r\n */\r\nexport function createTypedCore<T>(core: Core): TypedCore<T> {\r\n return {\r\n get: core.get.bind(core) as TypedCore<T>['get'],\r\n set: core.set.bind(core) as TypedCore<T>['set'],\r\n subscribe: core.subscribe.bind(core) as TypedCore<T>['subscribe'],\r\n batch: core.batch.bind(core),\r\n transaction: core.transaction.bind(core),\r\n computed: core.computed.bind(core) as TypedCore<T>['computed'],\r\n raw: core,\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime-internal.ts","../src/runtime/Node.ts","../src/runtime/Store.ts","../src/runtime/SubscriptionEngine.ts","../src/runtime/Batch.ts","../src/runtime/Transaction.ts","../src/runtime/CycleDetector.ts","../src/runtime/ComputedEngine.ts","../src/runtime/Scheduler.ts"],"sourcesContent":["/**\r\n * @internal — runtime primitives for engine authors\r\n *\r\n * This entry point exposes low-level runtime building blocks (Store,\r\n * SubscriptionEngine, Batch, Transaction, ComputedEngine, Scheduler). It is\r\n * intended for authors of custom engines who need primitives beyond what the\r\n * public `Core` interface provides.\r\n *\r\n * STABILITY: these exports are NOT part of the semver-stable public API.\r\n * Signatures may change between minor versions. Production code should prefer\r\n * the main `@skygraph/core` entry point and the `Core` interface.\r\n *\r\n * The runtime MUST NOT import from `engines/*`. If a future change requires\r\n * runtime to know about engine categories, that is an architecture violation\r\n * — push the feature into middleware or a dedicated extension surface.\r\n */\r\nexport {\r\n Store,\r\n createNode,\r\n SubscriptionEngine,\r\n Batch,\r\n Transaction,\r\n ComputedEngine,\r\n CycleDetector,\r\n Scheduler,\r\n} from './runtime/index'\r\n\r\nexport type {\r\n Node,\r\n Listener,\r\n Unsubscribe,\r\n StagedWrite,\r\n ComputeFn,\r\n Task,\r\n} from './runtime/index'\r\n","export interface Node {\n readonly id: string\n value: unknown\n version: number\n}\n\nexport function createNode(id: string, value: unknown): Node {\n return { id, value, version: 0 }\n}\n","import { createNode } from './Node'\nimport type { Node } from './Node'\n\nexport class Store {\n private nodes: Map<string, Node> = new Map()\n\n has(path: string): boolean {\n return this.nodes.has(path)\n }\n\n get(path: string): unknown {\n return this.nodes.get(path)?.value\n }\n\n getNode(path: string): Node | undefined {\n return this.nodes.get(path)\n }\n\n set(path: string, value: unknown): Node {\n let node = this.nodes.get(path)\n if (node) {\n node.value = value\n node.version++\n return node\n }\n node = createNode(path, value)\n this.nodes.set(path, node)\n return node\n }\n\n delete(path: string): boolean {\n return this.nodes.delete(path)\n }\n\n paths(): IterableIterator<string> {\n return this.nodes.keys()\n }\n\n size(): number {\n return this.nodes.size\n }\n}\n","export type Listener = (value: unknown) => void\nexport type Unsubscribe = () => void\n\nexport class SubscriptionEngine {\n private listeners: Map<string, Set<Listener>> = new Map()\n\n subscribe(path: string, cb: Listener): Unsubscribe {\n let set = this.listeners.get(path)\n if (!set) {\n set = new Set()\n this.listeners.set(path, set)\n }\n set.add(cb)\n\n return () => {\n set!.delete(cb)\n if (set!.size === 0) {\n this.listeners.delete(path)\n }\n }\n }\n\n notify(path: string, value: unknown): void {\n const set = this.listeners.get(path)\n if (!set) return\n for (const cb of set) {\n cb(value)\n }\n }\n\n notifyMany(changes: Map<string, unknown>): void {\n for (const [path, value] of changes) {\n this.notify(path, value)\n }\n }\n\n hasListeners(path: string): boolean {\n const set = this.listeners.get(path)\n return set !== undefined && set.size > 0\n }\n\n clear(): void {\n this.listeners.clear()\n }\n}\n","/**\n * Batch collects path changes during a batch callback\n * and defers notifications until the callback completes.\n */\nexport class Batch {\n private depth = 0\n private pending: Map<string, unknown> = new Map()\n\n get isActive(): boolean {\n return this.depth > 0\n }\n\n start(): void {\n this.depth++\n }\n\n end(): Map<string, unknown> | null {\n this.depth--\n if (this.depth > 0) return null\n\n if (this.pending.size === 0) return null\n\n const changes = new Map(this.pending)\n this.pending.clear()\n return changes\n }\n\n enqueue(path: string, value: unknown): void {\n this.pending.set(path, value)\n }\n}\n","import type { Store } from './Store'\n\nexport interface StagedWrite {\n path: string\n value: unknown\n prevValue: unknown\n}\n\n/**\n * Transaction implements the pipeline:\n * SET -> STAGE -> RESOLVE -> PROPAGATE -> COMMIT -> NOTIFY\n *\n * PROPAGATE is handled externally by ComputedEngine.\n * NOTIFY is handled externally by SubscriptionEngine via Batch.\n */\nexport class Transaction {\n private active = false\n private staged: StagedWrite[] = []\n\n get isActive(): boolean {\n return this.active\n }\n\n start(): void {\n if (this.active) {\n throw new Error('SkyGraph: nested transactions are not supported')\n }\n this.active = true\n this.staged = []\n }\n\n /** STAGE: record a write without mutating the store */\n stage(path: string, value: unknown, prevValue: unknown): void {\n this.staged.push({ path, value, prevValue })\n }\n\n /**\n * RESOLVE + COMMIT: drop unchanged values, apply the rest to store.\n * Returns the map of actually changed paths and their new values.\n */\n commit(store: Store): Map<string, unknown> {\n const changes = new Map<string, unknown>()\n\n for (const write of this.staged) {\n if (Object.is(write.value, write.prevValue)) continue\n store.set(write.path, write.value)\n changes.set(write.path, write.value)\n }\n\n this.active = false\n this.staged = []\n return changes\n }\n\n getStaged(): readonly StagedWrite[] {\n return this.staged\n }\n\n complete(): void {\n this.active = false\n this.staged = []\n }\n\n rollback(): void {\n this.active = false\n this.staged = []\n }\n}\n","/**\n * Detects cycles in the computed dependency graph using DFS.\n * Throws on cycle detection to prevent infinite recomputation.\n */\nexport class CycleDetector {\n private graph: Map<string, Set<string>> = new Map()\n\n addEdge(from: string, to: string): void {\n let deps = this.graph.get(from)\n if (!deps) {\n deps = new Set()\n this.graph.set(from, deps)\n }\n deps.add(to)\n\n if (this.hasCycle(to, new Set([from]))) {\n deps.delete(to)\n if (deps.size === 0) this.graph.delete(from)\n throw new Error(\n `SkyGraph: circular dependency detected: ${from} -> ${to}`\n )\n }\n }\n\n removeNode(id: string): void {\n this.graph.delete(id)\n for (const deps of this.graph.values()) {\n deps.delete(id)\n }\n }\n\n getDependents(id: string): string[] {\n const targets = this.graph.get(id)\n return targets ? [...targets] : []\n }\n\n private hasCycle(node: string, visited: Set<string>): boolean {\n if (visited.has(node)) return true\n const deps = this.graph.get(node)\n if (!deps) return false\n visited.add(node)\n for (const dep of deps) {\n if (this.hasCycle(dep, new Set(visited))) return true\n }\n return false\n }\n}\n","import { CycleDetector } from './CycleDetector'\nimport type { Store } from './Store'\n\nexport type ComputeFn = (...values: unknown[]) => unknown\n\ninterface ComputedNode {\n target: string\n deps: string[]\n fn: ComputeFn\n}\n\n/**\n * Manages computed (derived) values in the reactive graph.\n * When a dependency changes, the computed value is recomputed\n * and only propagated if the result actually changed.\n */\nexport class ComputedEngine {\n private computedNodes: Map<string, ComputedNode> = new Map()\n private cycleDetector = new CycleDetector()\n\n register(target: string, deps: string[], fn: ComputeFn): void {\n if (this.computedNodes.has(target)) {\n this.remove(target)\n }\n\n for (const dep of deps) {\n this.cycleDetector.addEdge(dep, target)\n }\n\n this.computedNodes.set(target, { target, deps, fn })\n }\n\n remove(target: string): void {\n this.computedNodes.delete(target)\n this.cycleDetector.removeNode(target)\n }\n\n isComputed(path: string): boolean {\n return this.computedNodes.has(path)\n }\n\n /**\n * Like propagate, but returns both new and old values for middleware support.\n * Does NOT write to the store — the caller handles that via dispatchWrite.\n */\n propagateRaw(\n changedPaths: Set<string>,\n store: Store,\n ): Map<string, { value: unknown; oldValue: unknown }> {\n const result = new Map<string, { value: unknown; oldValue: unknown }>()\n const visited = new Set<string>()\n const queue = [...changedPaths]\n\n while (queue.length > 0) {\n const path = queue.shift()!\n if (visited.has(path)) continue\n visited.add(path)\n\n const dependents = this.cycleDetector.getDependents(path)\n\n for (const depTarget of dependents) {\n const computed = this.computedNodes.get(depTarget)\n if (!computed) continue\n\n const depValues = computed.deps.map((d) => {\n const pending = result.get(d)\n return pending ? pending.value : store.get(d)\n })\n const newValue = computed.fn(...depValues)\n const oldValue = result.has(depTarget)\n ? result.get(depTarget)!.oldValue\n : store.get(depTarget)\n\n if (!Object.is(newValue, oldValue)) {\n result.set(depTarget, { value: newValue, oldValue })\n queue.push(depTarget)\n }\n }\n }\n\n return result\n }\n\n /**\n * Initialize all computed values based on current store state.\n */\n initializeAll(store: Store): void {\n for (const computed of this.computedNodes.values()) {\n const depValues = computed.deps.map((d) => store.get(d))\n const value = computed.fn(...depValues)\n store.set(computed.target, value)\n }\n }\n}\n","export type Task = () => void\n\n/**\n * Scheduler manages different priority queues for updates.\n * - micro: synchronous, within current transaction\n * - macro: deferred async effects (validation, server sync)\n * - render: batched UI notifications (grouped per frame)\n */\nexport class Scheduler {\n private microQueue: Task[] = []\n private macroQueue: Task[] = []\n private renderQueue: Task[] = []\n private renderScheduled = false\n\n enqueueMicro(task: Task): void {\n this.microQueue.push(task)\n }\n\n enqueueMacro(task: Task): void {\n this.macroQueue.push(task)\n queueMicrotask(() => this.flushMacro())\n }\n\n enqueueRender(task: Task): void {\n this.renderQueue.push(task)\n if (!this.renderScheduled) {\n this.renderScheduled = true\n if (typeof requestAnimationFrame !== 'undefined') {\n requestAnimationFrame(() => this.flushRender())\n } else {\n queueMicrotask(() => this.flushRender())\n }\n }\n }\n\n flushMicro(): void {\n while (this.microQueue.length > 0) {\n const task = this.microQueue.shift()!\n task()\n }\n }\n\n private flushMacro(): void {\n const tasks = [...this.macroQueue]\n this.macroQueue = []\n for (const task of tasks) {\n task()\n }\n }\n\n private flushRender(): void {\n this.renderScheduled = false\n const tasks = [...this.renderQueue]\n this.renderQueue = []\n for (const task of tasks) {\n task()\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/runtime-internal.ts","../src/runtime/Node.ts","../src/runtime/Store.ts","../src/runtime/SubscriptionEngine.ts","../src/runtime/Batch.ts","../src/runtime/Transaction.ts","../src/runtime/CycleDetector.ts","../src/runtime/ComputedEngine.ts","../src/runtime/Scheduler.ts"],"sourcesContent":["/**\r\n * @internal — runtime primitives for engine authors\r\n *\r\n * This entry point exposes low-level runtime building blocks (Store,\r\n * SubscriptionEngine, Batch, Transaction, ComputedEngine, Scheduler). It is\r\n * intended for authors of custom engines who need primitives beyond what the\r\n * public `Core` interface provides.\r\n *\r\n * STABILITY: these exports are NOT part of the semver-stable public API.\r\n * Signatures may change between minor versions. Production code should prefer\r\n * the main `@skygraph/core` entry point and the `Core` interface.\r\n *\r\n * The runtime MUST NOT import from `engines/*`. If a future change requires\r\n * runtime to know about engine categories, that is an architecture violation\r\n * — push the feature into middleware or a dedicated extension surface.\r\n */\r\nexport {\r\n Store,\r\n createNode,\r\n SubscriptionEngine,\r\n Batch,\r\n Transaction,\r\n ComputedEngine,\r\n CycleDetector,\r\n Scheduler,\r\n} from './runtime/index'\r\n\r\nexport type {\r\n Node,\r\n Listener,\r\n Unsubscribe,\r\n StagedWrite,\r\n ComputeFn,\r\n Task,\r\n} from './runtime/index'\r\n","export interface Node {\r\n readonly id: string\r\n value: unknown\r\n version: number\r\n}\r\n\r\nexport function createNode(id: string, value: unknown): Node {\r\n return { id, value, version: 0 }\r\n}\r\n","import { createNode } from './Node'\r\nimport type { Node } from './Node'\r\n\r\nexport class Store {\r\n private nodes: Map<string, Node> = new Map()\r\n\r\n has(path: string): boolean {\r\n return this.nodes.has(path)\r\n }\r\n\r\n get(path: string): unknown {\r\n return this.nodes.get(path)?.value\r\n }\r\n\r\n getNode(path: string): Node | undefined {\r\n return this.nodes.get(path)\r\n }\r\n\r\n set(path: string, value: unknown): Node {\r\n let node = this.nodes.get(path)\r\n if (node) {\r\n node.value = value\r\n node.version++\r\n return node\r\n }\r\n node = createNode(path, value)\r\n this.nodes.set(path, node)\r\n return node\r\n }\r\n\r\n delete(path: string): boolean {\r\n return this.nodes.delete(path)\r\n }\r\n\r\n paths(): IterableIterator<string> {\r\n return this.nodes.keys()\r\n }\r\n\r\n size(): number {\r\n return this.nodes.size\r\n }\r\n}\r\n","export type Listener = (value: unknown) => void\r\nexport type Unsubscribe = () => void\r\n\r\nexport class SubscriptionEngine {\r\n private listeners: Map<string, Set<Listener>> = new Map()\r\n\r\n subscribe(path: string, cb: Listener): Unsubscribe {\r\n let set = this.listeners.get(path)\r\n if (!set) {\r\n set = new Set()\r\n this.listeners.set(path, set)\r\n }\r\n set.add(cb)\r\n\r\n return () => {\r\n set!.delete(cb)\r\n if (set!.size === 0) {\r\n this.listeners.delete(path)\r\n }\r\n }\r\n }\r\n\r\n notify(path: string, value: unknown): void {\r\n const set = this.listeners.get(path)\r\n if (!set) return\r\n for (const cb of set) {\r\n cb(value)\r\n }\r\n }\r\n\r\n notifyMany(changes: Map<string, unknown>): void {\r\n for (const [path, value] of changes) {\r\n this.notify(path, value)\r\n }\r\n }\r\n\r\n hasListeners(path: string): boolean {\r\n const set = this.listeners.get(path)\r\n return set !== undefined && set.size > 0\r\n }\r\n\r\n clear(): void {\r\n this.listeners.clear()\r\n }\r\n}\r\n","/**\r\n * Batch collects path changes during a batch callback\r\n * and defers notifications until the callback completes.\r\n */\r\nexport class Batch {\r\n private depth = 0\r\n private pending: Map<string, unknown> = new Map()\r\n\r\n get isActive(): boolean {\r\n return this.depth > 0\r\n }\r\n\r\n start(): void {\r\n this.depth++\r\n }\r\n\r\n end(): Map<string, unknown> | null {\r\n this.depth--\r\n if (this.depth > 0) return null\r\n\r\n if (this.pending.size === 0) return null\r\n\r\n const changes = new Map(this.pending)\r\n this.pending.clear()\r\n return changes\r\n }\r\n\r\n enqueue(path: string, value: unknown): void {\r\n this.pending.set(path, value)\r\n }\r\n}\r\n","import type { Store } from './Store'\r\n\r\nexport interface StagedWrite {\r\n path: string\r\n value: unknown\r\n prevValue: unknown\r\n}\r\n\r\n/**\r\n * Transaction implements the pipeline:\r\n * SET -> STAGE -> RESOLVE -> PROPAGATE -> COMMIT -> NOTIFY\r\n *\r\n * PROPAGATE is handled externally by ComputedEngine.\r\n * NOTIFY is handled externally by SubscriptionEngine via Batch.\r\n */\r\nexport class Transaction {\r\n private active = false\r\n private staged: StagedWrite[] = []\r\n\r\n get isActive(): boolean {\r\n return this.active\r\n }\r\n\r\n start(): void {\r\n if (this.active) {\r\n throw new Error('SkyGraph: nested transactions are not supported')\r\n }\r\n this.active = true\r\n this.staged = []\r\n }\r\n\r\n /** STAGE: record a write without mutating the store */\r\n stage(path: string, value: unknown, prevValue: unknown): void {\r\n this.staged.push({ path, value, prevValue })\r\n }\r\n\r\n /**\r\n * RESOLVE + COMMIT: drop unchanged values, apply the rest to store.\r\n * Returns the map of actually changed paths and their new values.\r\n */\r\n commit(store: Store): Map<string, unknown> {\r\n const changes = new Map<string, unknown>()\r\n\r\n for (const write of this.staged) {\r\n if (Object.is(write.value, write.prevValue)) continue\r\n store.set(write.path, write.value)\r\n changes.set(write.path, write.value)\r\n }\r\n\r\n this.active = false\r\n this.staged = []\r\n return changes\r\n }\r\n\r\n getStaged(): readonly StagedWrite[] {\r\n return this.staged\r\n }\r\n\r\n complete(): void {\r\n this.active = false\r\n this.staged = []\r\n }\r\n\r\n rollback(): void {\r\n this.active = false\r\n this.staged = []\r\n }\r\n}\r\n","/**\r\n * Detects cycles in the computed dependency graph using DFS.\r\n * Throws on cycle detection to prevent infinite recomputation.\r\n */\r\nexport class CycleDetector {\r\n private graph: Map<string, Set<string>> = new Map()\r\n\r\n addEdge(from: string, to: string): void {\r\n let deps = this.graph.get(from)\r\n if (!deps) {\r\n deps = new Set()\r\n this.graph.set(from, deps)\r\n }\r\n deps.add(to)\r\n\r\n if (this.hasCycle(to, new Set([from]))) {\r\n deps.delete(to)\r\n if (deps.size === 0) this.graph.delete(from)\r\n throw new Error(\r\n `SkyGraph: circular dependency detected: ${from} -> ${to}`\r\n )\r\n }\r\n }\r\n\r\n removeNode(id: string): void {\r\n this.graph.delete(id)\r\n for (const deps of this.graph.values()) {\r\n deps.delete(id)\r\n }\r\n }\r\n\r\n getDependents(id: string): string[] {\r\n const targets = this.graph.get(id)\r\n return targets ? [...targets] : []\r\n }\r\n\r\n private hasCycle(node: string, visited: Set<string>): boolean {\r\n if (visited.has(node)) return true\r\n const deps = this.graph.get(node)\r\n if (!deps) return false\r\n visited.add(node)\r\n for (const dep of deps) {\r\n if (this.hasCycle(dep, new Set(visited))) return true\r\n }\r\n return false\r\n }\r\n}\r\n","import { CycleDetector } from './CycleDetector'\r\nimport type { Store } from './Store'\r\n\r\nexport type ComputeFn = (...values: unknown[]) => unknown\r\n\r\ninterface ComputedNode {\r\n target: string\r\n deps: string[]\r\n fn: ComputeFn\r\n}\r\n\r\n/**\r\n * Manages computed (derived) values in the reactive graph.\r\n * When a dependency changes, the computed value is recomputed\r\n * and only propagated if the result actually changed.\r\n */\r\nexport class ComputedEngine {\r\n private computedNodes: Map<string, ComputedNode> = new Map()\r\n private cycleDetector = new CycleDetector()\r\n\r\n register(target: string, deps: string[], fn: ComputeFn): void {\r\n if (this.computedNodes.has(target)) {\r\n this.remove(target)\r\n }\r\n\r\n for (const dep of deps) {\r\n this.cycleDetector.addEdge(dep, target)\r\n }\r\n\r\n this.computedNodes.set(target, { target, deps, fn })\r\n }\r\n\r\n remove(target: string): void {\r\n this.computedNodes.delete(target)\r\n this.cycleDetector.removeNode(target)\r\n }\r\n\r\n isComputed(path: string): boolean {\r\n return this.computedNodes.has(path)\r\n }\r\n\r\n /**\r\n * Like propagate, but returns both new and old values for middleware support.\r\n * Does NOT write to the store — the caller handles that via dispatchWrite.\r\n */\r\n propagateRaw(\r\n changedPaths: Set<string>,\r\n store: Store,\r\n ): Map<string, { value: unknown; oldValue: unknown }> {\r\n const result = new Map<string, { value: unknown; oldValue: unknown }>()\r\n const visited = new Set<string>()\r\n const queue = [...changedPaths]\r\n\r\n while (queue.length > 0) {\r\n const path = queue.shift()!\r\n if (visited.has(path)) continue\r\n visited.add(path)\r\n\r\n const dependents = this.cycleDetector.getDependents(path)\r\n\r\n for (const depTarget of dependents) {\r\n const computed = this.computedNodes.get(depTarget)\r\n if (!computed) continue\r\n\r\n const depValues = computed.deps.map((d) => {\r\n const pending = result.get(d)\r\n return pending ? pending.value : store.get(d)\r\n })\r\n const newValue = computed.fn(...depValues)\r\n const oldValue = result.has(depTarget)\r\n ? result.get(depTarget)!.oldValue\r\n : store.get(depTarget)\r\n\r\n if (!Object.is(newValue, oldValue)) {\r\n result.set(depTarget, { value: newValue, oldValue })\r\n queue.push(depTarget)\r\n }\r\n }\r\n }\r\n\r\n return result\r\n }\r\n\r\n /**\r\n * Initialize all computed values based on current store state.\r\n */\r\n initializeAll(store: Store): void {\r\n for (const computed of this.computedNodes.values()) {\r\n const depValues = computed.deps.map((d) => store.get(d))\r\n const value = computed.fn(...depValues)\r\n store.set(computed.target, value)\r\n }\r\n }\r\n}\r\n","export type Task = () => void\r\n\r\n/**\r\n * Scheduler manages different priority queues for updates.\r\n * - micro: synchronous, within current transaction\r\n * - macro: deferred async effects (validation, server sync)\r\n * - render: batched UI notifications (grouped per frame)\r\n */\r\nexport class Scheduler {\r\n private microQueue: Task[] = []\r\n private macroQueue: Task[] = []\r\n private renderQueue: Task[] = []\r\n private renderScheduled = false\r\n\r\n enqueueMicro(task: Task): void {\r\n this.microQueue.push(task)\r\n }\r\n\r\n enqueueMacro(task: Task): void {\r\n this.macroQueue.push(task)\r\n queueMicrotask(() => this.flushMacro())\r\n }\r\n\r\n enqueueRender(task: Task): void {\r\n this.renderQueue.push(task)\r\n if (!this.renderScheduled) {\r\n this.renderScheduled = true\r\n if (typeof requestAnimationFrame !== 'undefined') {\r\n requestAnimationFrame(() => this.flushRender())\r\n } else {\r\n queueMicrotask(() => this.flushRender())\r\n }\r\n }\r\n }\r\n\r\n flushMicro(): void {\r\n while (this.microQueue.length > 0) {\r\n const task = this.microQueue.shift()!\r\n task()\r\n }\r\n }\r\n\r\n private flushMacro(): void {\r\n const tasks = [...this.macroQueue]\r\n this.macroQueue = []\r\n for (const task of tasks) {\r\n task()\r\n }\r\n }\r\n\r\n private flushRender(): void {\r\n this.renderScheduled = false\r\n const tasks = [...this.renderQueue]\r\n this.renderQueue = []\r\n for (const task of tasks) {\r\n task()\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
@@ -6,7 +6,7 @@ import {
6
6
  SubscriptionEngine,
7
7
  Transaction,
8
8
  createNode
9
- } from "./chunk-GEMALROQ.js";
9
+ } from "./chunk-75BQUWZC.js";
10
10
 
11
11
  // src/runtime/Scheduler.ts
12
12
  var Scheduler = class {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/Scheduler.ts"],"sourcesContent":["export type Task = () => void\n\n/**\n * Scheduler manages different priority queues for updates.\n * - micro: synchronous, within current transaction\n * - macro: deferred async effects (validation, server sync)\n * - render: batched UI notifications (grouped per frame)\n */\nexport class Scheduler {\n private microQueue: Task[] = []\n private macroQueue: Task[] = []\n private renderQueue: Task[] = []\n private renderScheduled = false\n\n enqueueMicro(task: Task): void {\n this.microQueue.push(task)\n }\n\n enqueueMacro(task: Task): void {\n this.macroQueue.push(task)\n queueMicrotask(() => this.flushMacro())\n }\n\n enqueueRender(task: Task): void {\n this.renderQueue.push(task)\n if (!this.renderScheduled) {\n this.renderScheduled = true\n if (typeof requestAnimationFrame !== 'undefined') {\n requestAnimationFrame(() => this.flushRender())\n } else {\n queueMicrotask(() => this.flushRender())\n }\n }\n }\n\n flushMicro(): void {\n while (this.microQueue.length > 0) {\n const task = this.microQueue.shift()!\n task()\n }\n }\n\n private flushMacro(): void {\n const tasks = [...this.macroQueue]\n this.macroQueue = []\n for (const task of tasks) {\n task()\n }\n }\n\n private flushRender(): void {\n this.renderScheduled = false\n const tasks = [...this.renderQueue]\n this.renderQueue = []\n for (const task of tasks) {\n task()\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AAQO,IAAM,YAAN,MAAgB;AAAA,EACb,aAAqB,CAAC;AAAA,EACtB,aAAqB,CAAC;AAAA,EACtB,cAAsB,CAAC;AAAA,EACvB,kBAAkB;AAAA,EAE1B,aAAa,MAAkB;AAC7B,SAAK,WAAW,KAAK,IAAI;AAAA,EAC3B;AAAA,EAEA,aAAa,MAAkB;AAC7B,SAAK,WAAW,KAAK,IAAI;AACzB,mBAAe,MAAM,KAAK,WAAW,CAAC;AAAA,EACxC;AAAA,EAEA,cAAc,MAAkB;AAC9B,SAAK,YAAY,KAAK,IAAI;AAC1B,QAAI,CAAC,KAAK,iBAAiB;AACzB,WAAK,kBAAkB;AACvB,UAAI,OAAO,0BAA0B,aAAa;AAChD,8BAAsB,MAAM,KAAK,YAAY,CAAC;AAAA,MAChD,OAAO;AACL,uBAAe,MAAM,KAAK,YAAY,CAAC;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,aAAmB;AACjB,WAAO,KAAK,WAAW,SAAS,GAAG;AACjC,YAAM,OAAO,KAAK,WAAW,MAAM;AACnC,WAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEQ,aAAmB;AACzB,UAAM,QAAQ,CAAC,GAAG,KAAK,UAAU;AACjC,SAAK,aAAa,CAAC;AACnB,eAAW,QAAQ,OAAO;AACxB,WAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEQ,cAAoB;AAC1B,SAAK,kBAAkB;AACvB,UAAM,QAAQ,CAAC,GAAG,KAAK,WAAW;AAClC,SAAK,cAAc,CAAC;AACpB,eAAW,QAAQ,OAAO;AACxB,WAAK;AAAA,IACP;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/runtime/Scheduler.ts"],"sourcesContent":["export type Task = () => void\r\n\r\n/**\r\n * Scheduler manages different priority queues for updates.\r\n * - micro: synchronous, within current transaction\r\n * - macro: deferred async effects (validation, server sync)\r\n * - render: batched UI notifications (grouped per frame)\r\n */\r\nexport class Scheduler {\r\n private microQueue: Task[] = []\r\n private macroQueue: Task[] = []\r\n private renderQueue: Task[] = []\r\n private renderScheduled = false\r\n\r\n enqueueMicro(task: Task): void {\r\n this.microQueue.push(task)\r\n }\r\n\r\n enqueueMacro(task: Task): void {\r\n this.macroQueue.push(task)\r\n queueMicrotask(() => this.flushMacro())\r\n }\r\n\r\n enqueueRender(task: Task): void {\r\n this.renderQueue.push(task)\r\n if (!this.renderScheduled) {\r\n this.renderScheduled = true\r\n if (typeof requestAnimationFrame !== 'undefined') {\r\n requestAnimationFrame(() => this.flushRender())\r\n } else {\r\n queueMicrotask(() => this.flushRender())\r\n }\r\n }\r\n }\r\n\r\n flushMicro(): void {\r\n while (this.microQueue.length > 0) {\r\n const task = this.microQueue.shift()!\r\n task()\r\n }\r\n }\r\n\r\n private flushMacro(): void {\r\n const tasks = [...this.macroQueue]\r\n this.macroQueue = []\r\n for (const task of tasks) {\r\n task()\r\n }\r\n }\r\n\r\n private flushRender(): void {\r\n this.renderScheduled = false\r\n const tasks = [...this.renderQueue]\r\n this.renderQueue = []\r\n for (const task of tasks) {\r\n task()\r\n }\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;AAQO,IAAM,YAAN,MAAgB;AAAA,EACb,aAAqB,CAAC;AAAA,EACtB,aAAqB,CAAC;AAAA,EACtB,cAAsB,CAAC;AAAA,EACvB,kBAAkB;AAAA,EAE1B,aAAa,MAAkB;AAC7B,SAAK,WAAW,KAAK,IAAI;AAAA,EAC3B;AAAA,EAEA,aAAa,MAAkB;AAC7B,SAAK,WAAW,KAAK,IAAI;AACzB,mBAAe,MAAM,KAAK,WAAW,CAAC;AAAA,EACxC;AAAA,EAEA,cAAc,MAAkB;AAC9B,SAAK,YAAY,KAAK,IAAI;AAC1B,QAAI,CAAC,KAAK,iBAAiB;AACzB,WAAK,kBAAkB;AACvB,UAAI,OAAO,0BAA0B,aAAa;AAChD,8BAAsB,MAAM,KAAK,YAAY,CAAC;AAAA,MAChD,OAAO;AACL,uBAAe,MAAM,KAAK,YAAY,CAAC;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,aAAmB;AACjB,WAAO,KAAK,WAAW,SAAS,GAAG;AACjC,YAAM,OAAO,KAAK,WAAW,MAAM;AACnC,WAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEQ,aAAmB;AACzB,UAAM,QAAQ,CAAC,GAAG,KAAK,UAAU;AACjC,SAAK,aAAa,CAAC;AACnB,eAAW,QAAQ,OAAO;AACxB,WAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEQ,cAAoB;AAC1B,SAAK,kBAAkB;AACvB,UAAM,QAAQ,CAAC,GAAG,KAAK,WAAW;AAClC,SAAK,cAAc,CAAC;AACpB,eAAW,QAAQ,OAAO;AACxB,WAAK;AAAA,IACP;AAAA,EACF;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engines/table/index.ts","../src/engines/namespaces.ts","../src/engines/table/filter.ts","../src/engines/table/TableEngine.ts"],"sourcesContent":["export { createTable } from './TableEngine'\nexport {\n matchesOperator,\n matchesColumnFilter,\n resolveOperator,\n computeAggregate,\n} from './filter'\nexport type {\n TableEngine,\n TableState,\n TableOptions,\n ExportOptions,\n RowId,\n SortConfig,\n SortDirection,\n ColumnFilter,\n FilterFn,\n FilterOperator,\n AdvancedFilter,\n AggregateType,\n AggregateFn,\n ColumnAggregate,\n TableGroup,\n} from './types'\n","/**\n * Engine path namespace registry\n *\n * Every engine that stores state in the Core store under a `$`-prefixed\n * namespace MUST register its prefix here. This prevents two engines from\n * silently colliding on the same root path (e.g. two engines both writing\n * under `$meta.`).\n *\n * Rules:\n * 1. Prefixes MUST start with `$` and end with `.`.\n * 2. Prefixes MUST be unique across all engines.\n * 3. An engine MUST use `reservedPrefix(name)` at module load so\n * collisions surface at import time during dev/test.\n * 4. User data MUST NOT live under `$` prefixes — reserve them for engines.\n */\n\nconst reserved = new Map<string, string>()\n\n/**\n * Reserve (or look up) a prefix for an engine. Throws synchronously if the\n * same prefix is already owned by a different engine.\n */\nexport function reservePrefix(engine: string, prefix: string): string {\n if (!prefix.startsWith('$') || !prefix.endsWith('.')) {\n throw new Error(\n `[skygraph] engine \"${engine}\" tried to reserve invalid prefix \"${prefix}\". ` +\n `Engine prefixes must start with \"$\" and end with \".\".`,\n )\n }\n\n const existing = reserved.get(prefix)\n if (existing !== undefined && existing !== engine) {\n throw new Error(\n `[skygraph] prefix \"${prefix}\" is already owned by engine \"${existing}\", ` +\n `cannot reserve it for \"${engine}\". ` +\n `Pick a different prefix or update the registry.`,\n )\n }\n\n reserved.set(prefix, engine)\n return prefix\n}\n\n/** Snapshot of registered engine prefixes (debug / introspection). */\nexport function listReservedPrefixes(): ReadonlyMap<string, string> {\n return new Map(reserved)\n}\n\n/** Test-only helper. DO NOT call from engine code. */\nexport function __resetPrefixes(): void {\n reserved.clear()\n}\n\n// ── Canonical in-tree reservations ─────────────────────────────────────────\n//\n// Listed here so they are reserved even if the engine module is not loaded.\n// New engines must add their entry here and reference it from their\n// implementation via `reservePrefix(...)`.\n\nexport const FORM_META_PREFIX = reservePrefix('form', '$meta.')\nexport const TABLE_PREFIX = reservePrefix('table', '$table.')\nexport const TREE_PREFIX = reservePrefix('tree', '$tree.')\nexport const GRAPH_PREFIX = reservePrefix('graph', '$graph.')\nexport const CALENDAR_PREFIX = reservePrefix('calendar', '$calendar.')\n","import type {\n AdvancedFilter,\n AggregateFn,\n AggregateType,\n ColumnFilter,\n FilterOperator,\n} from './types'\n\n/**\n * Возвращает финальный оператор фильтра. Соблюдаем backward-compat:\n * если оператор не задан, и `value` — массив, считаем фильтр `in`,\n * иначе — `eq`.\n */\nexport function resolveOperator(\n filter: AdvancedFilter | ColumnFilter,\n): FilterOperator {\n const adv = filter as AdvancedFilter\n if (adv.op != null) return adv.op\n const col = filter as ColumnFilter\n if (col.operator != null) return col.operator\n return Array.isArray(filter.value) ? 'in' : 'eq'\n}\n\n/** Проверка пустоты для `isEmpty` / `isNotEmpty`. */\nfunction isEmptyValue(val: unknown): boolean {\n return val == null || val === ''\n}\n\n/** Числовое сравнение с защитой от NaN. */\nfunction asNumberPair(a: unknown, b: unknown): [number, number] | null {\n const na = typeof a === 'number' ? a : Number(a)\n const nb = typeof b === 'number' ? b : Number(b)\n if (Number.isNaN(na) || Number.isNaN(nb)) return null\n return [na, nb]\n}\n\n/**\n * Проверяет, проходит ли значение `val` фильтр (`op` + `filterValue`).\n * Используется как для `ColumnFilter`, так и для `AdvancedFilter`.\n */\nexport function matchesOperator(\n val: unknown,\n op: FilterOperator,\n filterValue: unknown,\n): boolean {\n switch (op) {\n case 'eq':\n return val === filterValue\n case 'neq':\n return val !== filterValue\n\n case 'lt':\n case 'lte':\n case 'gt':\n case 'gte': {\n const pair = asNumberPair(val, filterValue)\n if (!pair) return false\n const [a, b] = pair\n if (op === 'lt') return a < b\n if (op === 'lte') return a <= b\n if (op === 'gt') return a > b\n return a >= b\n }\n\n case 'between': {\n if (!Array.isArray(filterValue) || filterValue.length !== 2) return false\n const minPair = asNumberPair(val, filterValue[0])\n const maxPair = asNumberPair(val, filterValue[1])\n if (!minPair || !maxPair) return false\n return minPair[0] >= minPair[1] && maxPair[0] <= maxPair[1]\n }\n\n case 'in':\n return Array.isArray(filterValue) && filterValue.includes(val)\n case 'notIn':\n return Array.isArray(filterValue) && !filterValue.includes(val)\n\n case 'contains':\n if (typeof val !== 'string') return false\n return val.toLowerCase().includes(String(filterValue ?? '').toLowerCase())\n case 'startsWith':\n if (typeof val !== 'string') return false\n return val.toLowerCase().startsWith(String(filterValue ?? '').toLowerCase())\n case 'endsWith':\n if (typeof val !== 'string') return false\n return val.toLowerCase().endsWith(String(filterValue ?? '').toLowerCase())\n\n case 'isEmpty':\n return isEmptyValue(val)\n case 'isNotEmpty':\n return !isEmptyValue(val)\n }\n}\n\n/**\n * Применяет `ColumnFilter` к строке (по `filter.column`).\n * Точка входа из движка таблицы.\n */\nexport function matchesColumnFilter(\n row: Record<string, unknown>,\n filter: ColumnFilter,\n): boolean {\n const op = resolveOperator(filter)\n return matchesOperator(row[filter.column], op, filter.value)\n}\n\n/** Считает встроенную агрегацию для массива значений. */\nexport function computeAggregate(\n values: unknown[],\n type: AggregateType | AggregateFn,\n): unknown {\n if (typeof type === 'function') return type(values)\n\n if (type === 'count') return values.length\n\n const nums: number[] = []\n for (const v of values) {\n if (v == null || v === '') continue\n const n = typeof v === 'number' ? v : Number(v)\n if (!Number.isNaN(n)) nums.push(n)\n }\n if (nums.length === 0) return null\n\n switch (type) {\n case 'sum':\n return nums.reduce((a, b) => a + b, 0)\n case 'avg':\n return nums.reduce((a, b) => a + b, 0) / nums.length\n case 'min':\n return Math.min(...nums)\n case 'max':\n return Math.max(...nums)\n }\n}\n","import type { Core } from '../../types'\nimport type {\n AdvancedFilter,\n ColumnAggregate,\n ColumnFilter,\n ExportOptions,\n FilterFn,\n RowId,\n SortConfig,\n SortDirection,\n TableEngine,\n TableGroup,\n TableOptions,\n TableState,\n} from './types'\n\nimport { TABLE_PREFIX } from '../namespaces'\nimport { computeAggregate, matchesColumnFilter } from './filter'\n\nfunction statePath(tableId: string, key: string): string {\n return `${TABLE_PREFIX}${tableId}.state.${key}`\n}\n\nlet tableCounter = 0\n\n/**\n * `createTable` строит TableEngine поверх Core. Пользовательские данные строк\n * хранятся в private замыкании (`dataById`/`keysById`), а в Core попадают\n * только пути `$table.<id>.state.*` (агрегированные счётчики, ширины колонок,\n * пин-конфиг). Это держит `addRows(100k)` на масштабе O(N) Map.set вместо\n * O(N × cols) `core.set` (исторический паттерн `byId.<rid>.<col>` создавал\n * сотни тысяч узлов в Store и аллокаций Set/Map в `Core.set` на каждую\n * cell-запись — см. `docs/_streams/findings/T-Table-Perf.md` H1).\n *\n * Семантика публичных методов `getRow`/`getCell`/`getVisibleRows` сохранена.\n * Внешние подписки на `$table.<id>.state.*` пути продолжают работать;\n * подписок на `byId.*` в монорепе нет (они никогда не были задокументированы).\n */\nexport function createTable(core: Core, options?: TableOptions): TableEngine {\n const tableId = `t${tableCounter++}`\n let currentPageSize = options?.pageSize ?? 50\n\n let order: RowId[] = []\n let filteredOrder: RowId[] = []\n let sorts: SortConfig[] = []\n let filters: ColumnFilter[] = []\n let customFilterFn: FilterFn | null = null\n let page = 1\n let pinnedLeft: string[] = []\n let pinnedRight: string[] = []\n let columnWidths: Record<string, number> = {}\n\n // Группировка\n let groupByField: string | null = null\n let columnAggregates: ColumnAggregate[] = []\n let groups: TableGroup[] = []\n\n // Private storage: данные строк живут в замыкании, не в Core store.\n // Это устраняет 800k `core.set` per-cell на 100k×7 датасете и убирает\n // основной источник аллокаций (Store-узлы + Set/Map в `Core.set`).\n const dataById = new Map<RowId, Record<string, unknown>>()\n const keysById = new Map<RowId, string[]>()\n\n function getRowData(id: RowId): Record<string, unknown> | undefined {\n const stored = dataById.get(id)\n if (!stored) return undefined\n\n // Если пользователь передал явный whitelist колонок через\n // `TableOptions.columns`, отдаём отфильтрованную копию (исторический\n // контракт). В типичном кейсе (опция не задана) — возвращаем хранимую\n // ссылку без копии: hot path `compareBySort`/`recomputeFiltered`/render\n // не должны платить за per-row allocation.\n if (options?.columns) {\n const filtered: Record<string, unknown> = {}\n for (const col of options.columns) {\n filtered[col] = stored[col]\n }\n return filtered\n }\n return stored\n }\n\n function matchesFilters(rowData: Record<string, unknown>): boolean {\n if (customFilterFn && !customFilterFn(rowData)) return false\n\n for (const f of filters) {\n if (!matchesColumnFilter(rowData, f)) return false\n }\n return true\n }\n\n function compareBySort(a: RowId, b: RowId): number {\n const aData = getRowData(a)\n const bData = getRowData(b)\n\n for (const s of sorts) {\n const aVal = aData?.[s.column]\n const bVal = bData?.[s.column]\n\n if (aVal === bVal) continue\n if (aVal === undefined || aVal === null) return 1\n if (bVal === undefined || bVal === null) return -1\n\n const cmp = aVal < bVal ? -1 : 1\n return s.direction === 'asc' ? cmp : -cmp\n }\n return 0\n }\n\n function recomputeFiltered(): void {\n if (filters.length === 0 && !customFilterFn) {\n filteredOrder = [...order]\n } else {\n filteredOrder = order.filter((id) => {\n const data = getRowData(id)\n return data ? matchesFilters(data) : false\n })\n }\n\n if (sorts.length > 0) {\n filteredOrder.sort(compareBySort)\n }\n\n const totalPages = Math.max(1, Math.ceil(filteredOrder.length / currentPageSize))\n if (page > totalPages) page = totalPages\n\n recomputeGroups()\n publishState()\n }\n\n function recomputeGroups(): void {\n if (!groupByField) {\n groups = []\n return\n }\n\n const map = new Map<string, { value: unknown; rows: RowId[] }>()\n const orderKeys: string[] = []\n\n for (const id of filteredOrder) {\n const data = getRowData(id)\n if (!data) continue\n const raw = data[groupByField]\n const key = raw == null ? '__null__' : String(raw)\n if (!map.has(key)) {\n map.set(key, { value: raw, rows: [] })\n orderKeys.push(key)\n }\n map.get(key)!.rows.push(id)\n }\n\n groups = orderKeys.map((key) => {\n const { value, rows } = map.get(key)!\n const aggregates: Record<string, unknown> = {}\n for (const agg of columnAggregates) {\n const values = rows.map((id) => dataById.get(id)?.[agg.column])\n aggregates[agg.column] = computeAggregate(values, agg.type)\n }\n return { key, value, rows, aggregates }\n })\n }\n\n function publishState(): void {\n const totalPages = Math.max(1, Math.ceil(filteredOrder.length / currentPageSize))\n core.set(statePath(tableId, 'totalRows'), order.length)\n core.set(statePath(tableId, 'filteredRows'), filteredOrder.length)\n core.set(statePath(tableId, 'page'), page)\n core.set(statePath(tableId, 'pageSize'), currentPageSize)\n core.set(statePath(tableId, 'totalPages'), totalPages)\n core.set(statePath(tableId, 'groupBy'), groupByField)\n core.set(statePath(tableId, 'groupCount'), groups.length)\n }\n\n function getColumnKeys(): string[] {\n if (options?.columns) return options.columns\n if (order.length === 0) return []\n const firstId = order[0]\n return keysById.get(firstId) ?? []\n }\n\n /** Привести `AdvancedFilter | ColumnFilter` к единому `ColumnFilter`. */\n function toColumnFilter(column: string, filter: AdvancedFilter | ColumnFilter): ColumnFilter {\n const adv = filter as AdvancedFilter\n if (adv.op != null) {\n return { column, operator: adv.op, value: adv.value }\n }\n const cf = filter as ColumnFilter\n return { column, operator: cf.operator, value: cf.value }\n }\n\n const table: TableEngine = {\n addRow(id: RowId, data: Record<string, unknown>): void {\n dataById.set(id, data)\n keysById.set(id, Object.keys(data))\n order.push(id)\n recomputeFiltered()\n },\n\n addRows(rows: Array<{ id: RowId; data: Record<string, unknown> }>): void {\n // Single Map.set per row instead of (cols + 1) `core.set` per row —\n // на 100k×7 это разница 800k vs 100k операций и убирает\n // основной источник аллокаций. См. `findings/T-Table-Perf.md` H1.\n for (const row of rows) {\n dataById.set(row.id, row.data)\n keysById.set(row.id, Object.keys(row.data))\n order.push(row.id)\n }\n recomputeFiltered()\n },\n\n removeRow(id: RowId): void {\n const idx = order.indexOf(id)\n if (idx === -1) return\n order.splice(idx, 1)\n dataById.delete(id)\n keysById.delete(id)\n recomputeFiltered()\n },\n\n updateCell(id: RowId, column: string, value: unknown): void {\n const row = dataById.get(id)\n if (!row) return\n // Мутация хранимого объекта. `getRow` отдаёт эту же ссылку (см. контракт\n // в JSDoc к `createTable`), `recomputeFiltered` снизу пересоберёт сорт/\n // группы как раньше.\n row[column] = value\n const keys = keysById.get(id)\n if (keys && !keys.includes(column)) {\n keys.push(column)\n }\n recomputeFiltered()\n },\n\n getRow(id: RowId): Record<string, unknown> | undefined {\n return getRowData(id)\n },\n\n getCell(id: RowId, column: string): unknown {\n return dataById.get(id)?.[column]\n },\n\n getVisibleRows(): Array<{ id: RowId; data: Record<string, unknown> }> {\n const start = (page - 1) * currentPageSize\n const end = start + currentPageSize\n const slice = filteredOrder.slice(start, end)\n const out = new Array(slice.length) as Array<{\n id: RowId\n data: Record<string, unknown>\n }>\n for (let i = 0; i < slice.length; i++) {\n const id = slice[i]\n out[i] = { id, data: getRowData(id) ?? {} }\n }\n return out\n },\n\n getAllRowIds(): RowId[] {\n return [...order]\n },\n\n getAllRows(): Array<{ id: RowId; data: Record<string, unknown> }> {\n const out = new Array(filteredOrder.length) as Array<{\n id: RowId\n data: Record<string, unknown>\n }>\n for (let i = 0; i < filteredOrder.length; i++) {\n const id = filteredOrder[i]\n out[i] = { id, data: getRowData(id) ?? {} }\n }\n return out\n },\n\n setSort(column: string, direction: SortDirection, append?: boolean): void {\n if (append) {\n sorts = sorts.filter((s) => s.column !== column)\n sorts.push({ column, direction })\n } else {\n sorts = [{ column, direction }]\n }\n recomputeFiltered()\n },\n\n setSorts(newSorts: SortConfig[]): void {\n sorts = [...newSorts]\n recomputeFiltered()\n },\n\n clearSort(): void {\n sorts = []\n recomputeFiltered()\n },\n\n addFilter(filter: ColumnFilter): void {\n filters = filters.filter((f) => f.column !== filter.column)\n filters.push(filter)\n page = 1\n recomputeFiltered()\n },\n\n removeFilter(column: string): void {\n filters = filters.filter((f) => f.column !== column)\n recomputeFiltered()\n },\n\n clearFilters(): void {\n filters = []\n customFilterFn = null\n recomputeFiltered()\n },\n\n setFilterFn(fn: FilterFn | null): void {\n customFilterFn = fn\n recomputeFiltered()\n },\n\n setColumnFilter(column, filter): void {\n filters = filters.filter((f) => f.column !== column)\n if (filter !== null) {\n filters.push(toColumnFilter(column, filter))\n }\n page = 1\n recomputeFiltered()\n },\n\n setPage(p: number): void {\n const totalPages = Math.max(1, Math.ceil(filteredOrder.length / currentPageSize))\n page = Math.max(1, Math.min(p, totalPages))\n publishState()\n },\n\n setPageSize(size: number): void {\n currentPageSize = size\n page = 1\n recomputeFiltered()\n },\n\n nextPage(): void {\n table.setPage(page + 1)\n },\n\n prevPage(): void {\n table.setPage(page - 1)\n },\n\n moveRow(fromIndex: number, toIndex: number): void {\n if (fromIndex < 0 || fromIndex >= order.length) return\n if (toIndex < 0 || toIndex >= order.length) return\n if (fromIndex === toIndex) return\n\n const [item] = order.splice(fromIndex, 1)\n order.splice(toIndex, 0, item)\n recomputeFiltered()\n },\n\n pinColumn(column: string, side: 'left' | 'right' | null): void {\n pinnedLeft = pinnedLeft.filter((c) => c !== column)\n pinnedRight = pinnedRight.filter((c) => c !== column)\n if (side === 'left') pinnedLeft.push(column)\n else if (side === 'right') pinnedRight.push(column)\n core.set(statePath(tableId, 'pinnedLeft'), [...pinnedLeft])\n core.set(statePath(tableId, 'pinnedRight'), [...pinnedRight])\n },\n\n getPinnedColumns(): { left: string[]; right: string[] } {\n return { left: [...pinnedLeft], right: [...pinnedRight] }\n },\n\n clearPinned(): void {\n pinnedLeft = []\n pinnedRight = []\n core.set(statePath(tableId, 'pinnedLeft'), [])\n core.set(statePath(tableId, 'pinnedRight'), [])\n },\n\n setColumnWidth(column: string, width: number): void {\n columnWidths = { ...columnWidths, [column]: width }\n core.set(statePath(tableId, 'columnWidths'), { ...columnWidths })\n },\n\n getColumnWidth(column: string): number | undefined {\n return columnWidths[column]\n },\n\n getColumnWidths(): Record<string, number> {\n return { ...columnWidths }\n },\n\n clearColumnWidths(): void {\n columnWidths = {}\n core.set(statePath(tableId, 'columnWidths'), {})\n },\n\n moveRows(ids: RowId[], toIndex: number): void {\n const items = ids\n .map((id) => {\n const idx = order.indexOf(id)\n return { id, idx }\n })\n .filter((x) => x.idx !== -1)\n\n if (items.length === 0) return\n\n const idsSet = new Set(ids)\n const remaining = order.filter((id) => !idsSet.has(id))\n const clamped = Math.max(0, Math.min(toIndex, remaining.length))\n remaining.splice(clamped, 0, ...items.map((x) => x.id))\n order = remaining\n recomputeFiltered()\n },\n\n getExportData(exportOptions?: ExportOptions): string[][] {\n const cols = exportOptions?.columns ?? getColumnKeys()\n const result: string[][] = []\n\n if (exportOptions?.headers !== false) {\n result.push(cols)\n }\n\n const sourceIds = exportOptions?.allRows\n ? filteredOrder\n : filteredOrder.slice(\n (page - 1) * currentPageSize,\n (page - 1) * currentPageSize + currentPageSize,\n )\n\n for (const id of sourceIds) {\n const data = getRowData(id)\n if (!data) continue\n result.push(\n cols.map((col) => {\n const v = data[col]\n return v == null ? '' : String(v)\n }),\n )\n }\n\n return result\n },\n\n getTableState(): TableState {\n const totalPages = Math.max(1, Math.ceil(filteredOrder.length / currentPageSize))\n return {\n totalRows: order.length,\n filteredRows: filteredOrder.length,\n page,\n pageSize: currentPageSize,\n totalPages,\n sorts: [...sorts],\n sort: sorts.length > 0 ? sorts[0] : null,\n filters: [...filters],\n groupBy: groupByField,\n groupCount: groups.length,\n }\n },\n\n groupBy(field, aggregates): void {\n groupByField = field ?? null\n columnAggregates = aggregates ? [...aggregates] : []\n recomputeGroups()\n publishState()\n },\n\n clearGroupBy(): void {\n groupByField = null\n columnAggregates = []\n groups = []\n publishState()\n },\n\n getGroupBy(): string | null {\n return groupByField\n },\n\n getGroups(): TableGroup[] {\n return groups.map((g) => ({\n key: g.key,\n value: g.value,\n rows: [...g.rows],\n aggregates: { ...g.aggregates },\n }))\n },\n\n getEstimatedRowHeight(id: RowId): number | null {\n const fn = options?.estimateRowHeight\n if (!fn) return null\n const data = getRowData(id)\n if (!data) return null\n const v = fn(data, id)\n return Number.isFinite(v) ? v : null\n },\n\n reset(): void {\n order = []\n filteredOrder = []\n sorts = []\n filters = []\n customFilterFn = null\n page = 1\n pinnedLeft = []\n pinnedRight = []\n columnWidths = {}\n groupByField = null\n columnAggregates = []\n groups = []\n dataById.clear()\n keysById.clear()\n core.set(statePath(tableId, 'columnWidths'), {})\n publishState()\n },\n }\n\n // Initial publish — emit empty `columnWidths` so subscribers can read a\n // stable shape from the store without needing to handle `undefined`.\n core.set(statePath(tableId, 'columnWidths'), {})\n publishState()\n return table\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/engines/table/index.ts","../src/engines/namespaces.ts","../src/engines/table/filter.ts","../src/engines/table/TableEngine.ts"],"sourcesContent":["export { createTable } from './TableEngine'\r\nexport {\r\n matchesOperator,\r\n matchesColumnFilter,\r\n resolveOperator,\r\n computeAggregate,\r\n} from './filter'\r\nexport type {\r\n TableEngine,\r\n TableState,\r\n TableOptions,\r\n ExportOptions,\r\n RowId,\r\n SortConfig,\r\n SortDirection,\r\n ColumnFilter,\r\n FilterFn,\r\n FilterOperator,\r\n AdvancedFilter,\r\n AggregateType,\r\n AggregateFn,\r\n ColumnAggregate,\r\n TableGroup,\r\n} from './types'\r\n","/**\n * Engine path namespace registry\n *\n * Every engine that stores state in the Core store under a `$`-prefixed\n * namespace MUST register its prefix here. This prevents two engines from\n * silently colliding on the same root path (e.g. two engines both writing\n * under `$meta.`).\n *\n * Rules:\n * 1. Prefixes MUST start with `$` and end with `.`.\n * 2. Prefixes MUST be unique across all engines.\n * 3. An engine MUST use `reservedPrefix(name)` at module load so\n * collisions surface at import time during dev/test.\n * 4. User data MUST NOT live under `$` prefixes — reserve them for engines.\n */\n\nconst reserved = new Map<string, string>()\n\n/**\n * Reserve (or look up) a prefix for an engine. Throws synchronously if the\n * same prefix is already owned by a different engine.\n */\nexport function reservePrefix(engine: string, prefix: string): string {\n if (!prefix.startsWith('$') || !prefix.endsWith('.')) {\n throw new Error(\n `[skygraph] engine \"${engine}\" tried to reserve invalid prefix \"${prefix}\". ` +\n `Engine prefixes must start with \"$\" and end with \".\".`,\n )\n }\n\n const existing = reserved.get(prefix)\n if (existing !== undefined && existing !== engine) {\n throw new Error(\n `[skygraph] prefix \"${prefix}\" is already owned by engine \"${existing}\", ` +\n `cannot reserve it for \"${engine}\". ` +\n `Pick a different prefix or update the registry.`,\n )\n }\n\n reserved.set(prefix, engine)\n return prefix\n}\n\n/** Snapshot of registered engine prefixes (debug / introspection). */\nexport function listReservedPrefixes(): ReadonlyMap<string, string> {\n return new Map(reserved)\n}\n\n/** Test-only helper. DO NOT call from engine code. */\nexport function __resetPrefixes(): void {\n reserved.clear()\n}\n\n// ── Canonical in-tree reservations ─────────────────────────────────────────\n//\n// Listed here so they are reserved even if the engine module is not loaded.\n// New engines must add their entry here and reference it from their\n// implementation via `reservePrefix(...)`.\n\nexport const FORM_META_PREFIX = reservePrefix('form', '$meta.')\nexport const TABLE_PREFIX = reservePrefix('table', '$table.')\nexport const TREE_PREFIX = reservePrefix('tree', '$tree.')\nexport const GRAPH_PREFIX = reservePrefix('graph', '$graph.')\nexport const CALENDAR_PREFIX = reservePrefix('calendar', '$calendar.')\n","import type {\r\n AdvancedFilter,\r\n AggregateFn,\r\n AggregateType,\r\n ColumnFilter,\r\n FilterOperator,\r\n} from './types'\r\n\r\n/**\r\n * Возвращает финальный оператор фильтра. Соблюдаем backward-compat:\r\n * если оператор не задан, и `value` — массив, считаем фильтр `in`,\r\n * иначе — `eq`.\r\n */\r\nexport function resolveOperator(\r\n filter: AdvancedFilter | ColumnFilter,\r\n): FilterOperator {\r\n const adv = filter as AdvancedFilter\r\n if (adv.op != null) return adv.op\r\n const col = filter as ColumnFilter\r\n if (col.operator != null) return col.operator\r\n return Array.isArray(filter.value) ? 'in' : 'eq'\r\n}\r\n\r\n/** Проверка пустоты для `isEmpty` / `isNotEmpty`. */\r\nfunction isEmptyValue(val: unknown): boolean {\r\n return val == null || val === ''\r\n}\r\n\r\n/** Числовое сравнение с защитой от NaN. */\r\nfunction asNumberPair(a: unknown, b: unknown): [number, number] | null {\r\n const na = typeof a === 'number' ? a : Number(a)\r\n const nb = typeof b === 'number' ? b : Number(b)\r\n if (Number.isNaN(na) || Number.isNaN(nb)) return null\r\n return [na, nb]\r\n}\r\n\r\n/**\r\n * Проверяет, проходит ли значение `val` фильтр (`op` + `filterValue`).\r\n * Используется как для `ColumnFilter`, так и для `AdvancedFilter`.\r\n */\r\nexport function matchesOperator(\r\n val: unknown,\r\n op: FilterOperator,\r\n filterValue: unknown,\r\n): boolean {\r\n switch (op) {\r\n case 'eq':\r\n return val === filterValue\r\n case 'neq':\r\n return val !== filterValue\r\n\r\n case 'lt':\r\n case 'lte':\r\n case 'gt':\r\n case 'gte': {\r\n const pair = asNumberPair(val, filterValue)\r\n if (!pair) return false\r\n const [a, b] = pair\r\n if (op === 'lt') return a < b\r\n if (op === 'lte') return a <= b\r\n if (op === 'gt') return a > b\r\n return a >= b\r\n }\r\n\r\n case 'between': {\r\n if (!Array.isArray(filterValue) || filterValue.length !== 2) return false\r\n const minPair = asNumberPair(val, filterValue[0])\r\n const maxPair = asNumberPair(val, filterValue[1])\r\n if (!minPair || !maxPair) return false\r\n return minPair[0] >= minPair[1] && maxPair[0] <= maxPair[1]\r\n }\r\n\r\n case 'in':\r\n return Array.isArray(filterValue) && filterValue.includes(val)\r\n case 'notIn':\r\n return Array.isArray(filterValue) && !filterValue.includes(val)\r\n\r\n case 'contains':\r\n if (typeof val !== 'string') return false\r\n return val.toLowerCase().includes(String(filterValue ?? '').toLowerCase())\r\n case 'startsWith':\r\n if (typeof val !== 'string') return false\r\n return val.toLowerCase().startsWith(String(filterValue ?? '').toLowerCase())\r\n case 'endsWith':\r\n if (typeof val !== 'string') return false\r\n return val.toLowerCase().endsWith(String(filterValue ?? '').toLowerCase())\r\n\r\n case 'isEmpty':\r\n return isEmptyValue(val)\r\n case 'isNotEmpty':\r\n return !isEmptyValue(val)\r\n }\r\n}\r\n\r\n/**\r\n * Применяет `ColumnFilter` к строке (по `filter.column`).\r\n * Точка входа из движка таблицы.\r\n */\r\nexport function matchesColumnFilter(\r\n row: Record<string, unknown>,\r\n filter: ColumnFilter,\r\n): boolean {\r\n const op = resolveOperator(filter)\r\n return matchesOperator(row[filter.column], op, filter.value)\r\n}\r\n\r\n/** Считает встроенную агрегацию для массива значений. */\r\nexport function computeAggregate(\r\n values: unknown[],\r\n type: AggregateType | AggregateFn,\r\n): unknown {\r\n if (typeof type === 'function') return type(values)\r\n\r\n if (type === 'count') return values.length\r\n\r\n const nums: number[] = []\r\n for (const v of values) {\r\n if (v == null || v === '') continue\r\n const n = typeof v === 'number' ? v : Number(v)\r\n if (!Number.isNaN(n)) nums.push(n)\r\n }\r\n if (nums.length === 0) return null\r\n\r\n switch (type) {\r\n case 'sum':\r\n return nums.reduce((a, b) => a + b, 0)\r\n case 'avg':\r\n return nums.reduce((a, b) => a + b, 0) / nums.length\r\n case 'min':\r\n return Math.min(...nums)\r\n case 'max':\r\n return Math.max(...nums)\r\n }\r\n}\r\n","import type { Core } from '../../types'\r\nimport type {\r\n AdvancedFilter,\r\n ColumnAggregate,\r\n ColumnFilter,\r\n ExportOptions,\r\n FilterFn,\r\n RowId,\r\n SortConfig,\r\n SortDirection,\r\n TableEngine,\r\n TableGroup,\r\n TableOptions,\r\n TableState,\r\n} from './types'\r\n\r\nimport { TABLE_PREFIX } from '../namespaces'\r\nimport { computeAggregate, matchesColumnFilter } from './filter'\r\n\r\nfunction statePath(tableId: string, key: string): string {\r\n return `${TABLE_PREFIX}${tableId}.state.${key}`\r\n}\r\n\r\nlet tableCounter = 0\r\n\r\n/**\r\n * `createTable` строит TableEngine поверх Core. Пользовательские данные строк\r\n * хранятся в private замыкании (`dataById`/`keysById`), а в Core попадают\r\n * только пути `$table.<id>.state.*` (агрегированные счётчики, ширины колонок,\r\n * пин-конфиг). Это держит `addRows(100k)` на масштабе O(N) Map.set вместо\r\n * O(N × cols) `core.set` (исторический паттерн `byId.<rid>.<col>` создавал\r\n * сотни тысяч узлов в Store и аллокаций Set/Map в `Core.set` на каждую\r\n * cell-запись — см. `docs/_streams/findings/T-Table-Perf.md` H1).\r\n *\r\n * Семантика публичных методов `getRow`/`getCell`/`getVisibleRows` сохранена.\r\n * Внешние подписки на `$table.<id>.state.*` пути продолжают работать;\r\n * подписок на `byId.*` в монорепе нет (они никогда не были задокументированы).\r\n */\r\nexport function createTable(core: Core, options?: TableOptions): TableEngine {\r\n const tableId = `t${tableCounter++}`\r\n let currentPageSize = options?.pageSize ?? 50\r\n\r\n let order: RowId[] = []\r\n let filteredOrder: RowId[] = []\r\n let sorts: SortConfig[] = []\r\n let filters: ColumnFilter[] = []\r\n let customFilterFn: FilterFn | null = null\r\n let page = 1\r\n let pinnedLeft: string[] = []\r\n let pinnedRight: string[] = []\r\n let columnWidths: Record<string, number> = {}\r\n\r\n // Группировка\r\n let groupByField: string | null = null\r\n let columnAggregates: ColumnAggregate[] = []\r\n let groups: TableGroup[] = []\r\n\r\n // Private storage: данные строк живут в замыкании, не в Core store.\r\n // Это устраняет 800k `core.set` per-cell на 100k×7 датасете и убирает\r\n // основной источник аллокаций (Store-узлы + Set/Map в `Core.set`).\r\n const dataById = new Map<RowId, Record<string, unknown>>()\r\n const keysById = new Map<RowId, string[]>()\r\n\r\n function getRowData(id: RowId): Record<string, unknown> | undefined {\r\n const stored = dataById.get(id)\r\n if (!stored) return undefined\r\n\r\n // Если пользователь передал явный whitelist колонок через\r\n // `TableOptions.columns`, отдаём отфильтрованную копию (исторический\r\n // контракт). В типичном кейсе (опция не задана) — возвращаем хранимую\r\n // ссылку без копии: hot path `compareBySort`/`recomputeFiltered`/render\r\n // не должны платить за per-row allocation.\r\n if (options?.columns) {\r\n const filtered: Record<string, unknown> = {}\r\n for (const col of options.columns) {\r\n filtered[col] = stored[col]\r\n }\r\n return filtered\r\n }\r\n return stored\r\n }\r\n\r\n function matchesFilters(rowData: Record<string, unknown>): boolean {\r\n if (customFilterFn && !customFilterFn(rowData)) return false\r\n\r\n for (const f of filters) {\r\n if (!matchesColumnFilter(rowData, f)) return false\r\n }\r\n return true\r\n }\r\n\r\n function compareBySort(a: RowId, b: RowId): number {\r\n const aData = getRowData(a)\r\n const bData = getRowData(b)\r\n\r\n for (const s of sorts) {\r\n const aVal = aData?.[s.column]\r\n const bVal = bData?.[s.column]\r\n\r\n if (aVal === bVal) continue\r\n if (aVal === undefined || aVal === null) return 1\r\n if (bVal === undefined || bVal === null) return -1\r\n\r\n const cmp = aVal < bVal ? -1 : 1\r\n return s.direction === 'asc' ? cmp : -cmp\r\n }\r\n return 0\r\n }\r\n\r\n function recomputeFiltered(): void {\r\n if (filters.length === 0 && !customFilterFn) {\r\n filteredOrder = [...order]\r\n } else {\r\n filteredOrder = order.filter((id) => {\r\n const data = getRowData(id)\r\n return data ? matchesFilters(data) : false\r\n })\r\n }\r\n\r\n if (sorts.length > 0) {\r\n filteredOrder.sort(compareBySort)\r\n }\r\n\r\n const totalPages = Math.max(1, Math.ceil(filteredOrder.length / currentPageSize))\r\n if (page > totalPages) page = totalPages\r\n\r\n recomputeGroups()\r\n publishState()\r\n }\r\n\r\n function recomputeGroups(): void {\r\n if (!groupByField) {\r\n groups = []\r\n return\r\n }\r\n\r\n const map = new Map<string, { value: unknown; rows: RowId[] }>()\r\n const orderKeys: string[] = []\r\n\r\n for (const id of filteredOrder) {\r\n const data = getRowData(id)\r\n if (!data) continue\r\n const raw = data[groupByField]\r\n const key = raw == null ? '__null__' : String(raw)\r\n if (!map.has(key)) {\r\n map.set(key, { value: raw, rows: [] })\r\n orderKeys.push(key)\r\n }\r\n map.get(key)!.rows.push(id)\r\n }\r\n\r\n groups = orderKeys.map((key) => {\r\n const { value, rows } = map.get(key)!\r\n const aggregates: Record<string, unknown> = {}\r\n for (const agg of columnAggregates) {\r\n const values = rows.map((id) => dataById.get(id)?.[agg.column])\r\n aggregates[agg.column] = computeAggregate(values, agg.type)\r\n }\r\n return { key, value, rows, aggregates }\r\n })\r\n }\r\n\r\n function publishState(): void {\r\n const totalPages = Math.max(1, Math.ceil(filteredOrder.length / currentPageSize))\r\n core.set(statePath(tableId, 'totalRows'), order.length)\r\n core.set(statePath(tableId, 'filteredRows'), filteredOrder.length)\r\n core.set(statePath(tableId, 'page'), page)\r\n core.set(statePath(tableId, 'pageSize'), currentPageSize)\r\n core.set(statePath(tableId, 'totalPages'), totalPages)\r\n core.set(statePath(tableId, 'groupBy'), groupByField)\r\n core.set(statePath(tableId, 'groupCount'), groups.length)\r\n }\r\n\r\n function getColumnKeys(): string[] {\r\n if (options?.columns) return options.columns\r\n if (order.length === 0) return []\r\n const firstId = order[0]\r\n return keysById.get(firstId) ?? []\r\n }\r\n\r\n /** Привести `AdvancedFilter | ColumnFilter` к единому `ColumnFilter`. */\r\n function toColumnFilter(column: string, filter: AdvancedFilter | ColumnFilter): ColumnFilter {\r\n const adv = filter as AdvancedFilter\r\n if (adv.op != null) {\r\n return { column, operator: adv.op, value: adv.value }\r\n }\r\n const cf = filter as ColumnFilter\r\n return { column, operator: cf.operator, value: cf.value }\r\n }\r\n\r\n const table: TableEngine = {\r\n addRow(id: RowId, data: Record<string, unknown>): void {\r\n dataById.set(id, data)\r\n keysById.set(id, Object.keys(data))\r\n order.push(id)\r\n recomputeFiltered()\r\n },\r\n\r\n addRows(rows: Array<{ id: RowId; data: Record<string, unknown> }>): void {\r\n // Single Map.set per row instead of (cols + 1) `core.set` per row —\r\n // на 100k×7 это разница 800k vs 100k операций и убирает\r\n // основной источник аллокаций. См. `findings/T-Table-Perf.md` H1.\r\n for (const row of rows) {\r\n dataById.set(row.id, row.data)\r\n keysById.set(row.id, Object.keys(row.data))\r\n order.push(row.id)\r\n }\r\n recomputeFiltered()\r\n },\r\n\r\n removeRow(id: RowId): void {\r\n const idx = order.indexOf(id)\r\n if (idx === -1) return\r\n order.splice(idx, 1)\r\n dataById.delete(id)\r\n keysById.delete(id)\r\n recomputeFiltered()\r\n },\r\n\r\n updateCell(id: RowId, column: string, value: unknown): void {\r\n const row = dataById.get(id)\r\n if (!row) return\r\n // Мутация хранимого объекта. `getRow` отдаёт эту же ссылку (см. контракт\r\n // в JSDoc к `createTable`), `recomputeFiltered` снизу пересоберёт сорт/\r\n // группы как раньше.\r\n row[column] = value\r\n const keys = keysById.get(id)\r\n if (keys && !keys.includes(column)) {\r\n keys.push(column)\r\n }\r\n recomputeFiltered()\r\n },\r\n\r\n getRow(id: RowId): Record<string, unknown> | undefined {\r\n return getRowData(id)\r\n },\r\n\r\n getCell(id: RowId, column: string): unknown {\r\n return dataById.get(id)?.[column]\r\n },\r\n\r\n getVisibleRows(): Array<{ id: RowId; data: Record<string, unknown> }> {\r\n const start = (page - 1) * currentPageSize\r\n const end = start + currentPageSize\r\n const slice = filteredOrder.slice(start, end)\r\n const out = new Array(slice.length) as Array<{\r\n id: RowId\r\n data: Record<string, unknown>\r\n }>\r\n for (let i = 0; i < slice.length; i++) {\r\n const id = slice[i]\r\n out[i] = { id, data: getRowData(id) ?? {} }\r\n }\r\n return out\r\n },\r\n\r\n getAllRowIds(): RowId[] {\r\n return [...order]\r\n },\r\n\r\n getAllRows(): Array<{ id: RowId; data: Record<string, unknown> }> {\r\n const out = new Array(filteredOrder.length) as Array<{\r\n id: RowId\r\n data: Record<string, unknown>\r\n }>\r\n for (let i = 0; i < filteredOrder.length; i++) {\r\n const id = filteredOrder[i]\r\n out[i] = { id, data: getRowData(id) ?? {} }\r\n }\r\n return out\r\n },\r\n\r\n setSort(column: string, direction: SortDirection, append?: boolean): void {\r\n if (append) {\r\n sorts = sorts.filter((s) => s.column !== column)\r\n sorts.push({ column, direction })\r\n } else {\r\n sorts = [{ column, direction }]\r\n }\r\n recomputeFiltered()\r\n },\r\n\r\n setSorts(newSorts: SortConfig[]): void {\r\n sorts = [...newSorts]\r\n recomputeFiltered()\r\n },\r\n\r\n clearSort(): void {\r\n sorts = []\r\n recomputeFiltered()\r\n },\r\n\r\n addFilter(filter: ColumnFilter): void {\r\n filters = filters.filter((f) => f.column !== filter.column)\r\n filters.push(filter)\r\n page = 1\r\n recomputeFiltered()\r\n },\r\n\r\n removeFilter(column: string): void {\r\n filters = filters.filter((f) => f.column !== column)\r\n recomputeFiltered()\r\n },\r\n\r\n clearFilters(): void {\r\n filters = []\r\n customFilterFn = null\r\n recomputeFiltered()\r\n },\r\n\r\n setFilterFn(fn: FilterFn | null): void {\r\n customFilterFn = fn\r\n recomputeFiltered()\r\n },\r\n\r\n setColumnFilter(column, filter): void {\r\n filters = filters.filter((f) => f.column !== column)\r\n if (filter !== null) {\r\n filters.push(toColumnFilter(column, filter))\r\n }\r\n page = 1\r\n recomputeFiltered()\r\n },\r\n\r\n setPage(p: number): void {\r\n const totalPages = Math.max(1, Math.ceil(filteredOrder.length / currentPageSize))\r\n page = Math.max(1, Math.min(p, totalPages))\r\n publishState()\r\n },\r\n\r\n setPageSize(size: number): void {\r\n currentPageSize = size\r\n page = 1\r\n recomputeFiltered()\r\n },\r\n\r\n nextPage(): void {\r\n table.setPage(page + 1)\r\n },\r\n\r\n prevPage(): void {\r\n table.setPage(page - 1)\r\n },\r\n\r\n moveRow(fromIndex: number, toIndex: number): void {\r\n if (fromIndex < 0 || fromIndex >= order.length) return\r\n if (toIndex < 0 || toIndex >= order.length) return\r\n if (fromIndex === toIndex) return\r\n\r\n const [item] = order.splice(fromIndex, 1)\r\n order.splice(toIndex, 0, item)\r\n recomputeFiltered()\r\n },\r\n\r\n pinColumn(column: string, side: 'left' | 'right' | null): void {\r\n pinnedLeft = pinnedLeft.filter((c) => c !== column)\r\n pinnedRight = pinnedRight.filter((c) => c !== column)\r\n if (side === 'left') pinnedLeft.push(column)\r\n else if (side === 'right') pinnedRight.push(column)\r\n core.set(statePath(tableId, 'pinnedLeft'), [...pinnedLeft])\r\n core.set(statePath(tableId, 'pinnedRight'), [...pinnedRight])\r\n },\r\n\r\n getPinnedColumns(): { left: string[]; right: string[] } {\r\n return { left: [...pinnedLeft], right: [...pinnedRight] }\r\n },\r\n\r\n clearPinned(): void {\r\n pinnedLeft = []\r\n pinnedRight = []\r\n core.set(statePath(tableId, 'pinnedLeft'), [])\r\n core.set(statePath(tableId, 'pinnedRight'), [])\r\n },\r\n\r\n setColumnWidth(column: string, width: number): void {\r\n columnWidths = { ...columnWidths, [column]: width }\r\n core.set(statePath(tableId, 'columnWidths'), { ...columnWidths })\r\n },\r\n\r\n getColumnWidth(column: string): number | undefined {\r\n return columnWidths[column]\r\n },\r\n\r\n getColumnWidths(): Record<string, number> {\r\n return { ...columnWidths }\r\n },\r\n\r\n clearColumnWidths(): void {\r\n columnWidths = {}\r\n core.set(statePath(tableId, 'columnWidths'), {})\r\n },\r\n\r\n moveRows(ids: RowId[], toIndex: number): void {\r\n const items = ids\r\n .map((id) => {\r\n const idx = order.indexOf(id)\r\n return { id, idx }\r\n })\r\n .filter((x) => x.idx !== -1)\r\n\r\n if (items.length === 0) return\r\n\r\n const idsSet = new Set(ids)\r\n const remaining = order.filter((id) => !idsSet.has(id))\r\n const clamped = Math.max(0, Math.min(toIndex, remaining.length))\r\n remaining.splice(clamped, 0, ...items.map((x) => x.id))\r\n order = remaining\r\n recomputeFiltered()\r\n },\r\n\r\n getExportData(exportOptions?: ExportOptions): string[][] {\r\n const cols = exportOptions?.columns ?? getColumnKeys()\r\n const result: string[][] = []\r\n\r\n if (exportOptions?.headers !== false) {\r\n result.push(cols)\r\n }\r\n\r\n const sourceIds = exportOptions?.allRows\r\n ? filteredOrder\r\n : filteredOrder.slice(\r\n (page - 1) * currentPageSize,\r\n (page - 1) * currentPageSize + currentPageSize,\r\n )\r\n\r\n for (const id of sourceIds) {\r\n const data = getRowData(id)\r\n if (!data) continue\r\n result.push(\r\n cols.map((col) => {\r\n const v = data[col]\r\n return v == null ? '' : String(v)\r\n }),\r\n )\r\n }\r\n\r\n return result\r\n },\r\n\r\n getTableState(): TableState {\r\n const totalPages = Math.max(1, Math.ceil(filteredOrder.length / currentPageSize))\r\n return {\r\n totalRows: order.length,\r\n filteredRows: filteredOrder.length,\r\n page,\r\n pageSize: currentPageSize,\r\n totalPages,\r\n sorts: [...sorts],\r\n sort: sorts.length > 0 ? sorts[0] : null,\r\n filters: [...filters],\r\n groupBy: groupByField,\r\n groupCount: groups.length,\r\n }\r\n },\r\n\r\n groupBy(field, aggregates): void {\r\n groupByField = field ?? null\r\n columnAggregates = aggregates ? [...aggregates] : []\r\n recomputeGroups()\r\n publishState()\r\n },\r\n\r\n clearGroupBy(): void {\r\n groupByField = null\r\n columnAggregates = []\r\n groups = []\r\n publishState()\r\n },\r\n\r\n getGroupBy(): string | null {\r\n return groupByField\r\n },\r\n\r\n getGroups(): TableGroup[] {\r\n return groups.map((g) => ({\r\n key: g.key,\r\n value: g.value,\r\n rows: [...g.rows],\r\n aggregates: { ...g.aggregates },\r\n }))\r\n },\r\n\r\n getEstimatedRowHeight(id: RowId): number | null {\r\n const fn = options?.estimateRowHeight\r\n if (!fn) return null\r\n const data = getRowData(id)\r\n if (!data) return null\r\n const v = fn(data, id)\r\n return Number.isFinite(v) ? v : null\r\n },\r\n\r\n reset(): void {\r\n order = []\r\n filteredOrder = []\r\n sorts = []\r\n filters = []\r\n customFilterFn = null\r\n page = 1\r\n pinnedLeft = []\r\n pinnedRight = []\r\n columnWidths = {}\r\n groupByField = null\r\n columnAggregates = []\r\n groups = []\r\n dataById.clear()\r\n keysById.clear()\r\n core.set(statePath(tableId, 'columnWidths'), {})\r\n publishState()\r\n },\r\n }\r\n\r\n // Initial publish — emit empty `columnWidths` so subscribers can read a\r\n // stable shape from the store without needing to handle `undefined`.\r\n core.set(statePath(tableId, 'columnWidths'), {})\r\n publishState()\r\n return table\r\n}\r\n"],"mappings":"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","names":[]}
package/dist/table.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  matchesColumnFilter,
5
5
  matchesOperator,
6
6
  resolveOperator
7
- } from "./chunk-ZWB7JGAJ.js";
7
+ } from "./chunk-CEX4ZVZF.js";
8
8
  import "./chunk-5CCD5Q4B.js";
9
9
  export {
10
10
  computeAggregate,
package/dist/tree.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engines/tree/index.ts","../src/engines/namespaces.ts","../src/engines/tree/TreeEngine.ts"],"sourcesContent":["export { createTree } from './TreeEngine'\nexport type {\n TreeEngine,\n TreeState,\n TreeEngineOptions,\n TreeKey,\n TreeNodeData,\n TreeFieldNames,\n} from './types'\n","/**\n * Engine path namespace registry\n *\n * Every engine that stores state in the Core store under a `$`-prefixed\n * namespace MUST register its prefix here. This prevents two engines from\n * silently colliding on the same root path (e.g. two engines both writing\n * under `$meta.`).\n *\n * Rules:\n * 1. Prefixes MUST start with `$` and end with `.`.\n * 2. Prefixes MUST be unique across all engines.\n * 3. An engine MUST use `reservedPrefix(name)` at module load so\n * collisions surface at import time during dev/test.\n * 4. User data MUST NOT live under `$` prefixes — reserve them for engines.\n */\n\nconst reserved = new Map<string, string>()\n\n/**\n * Reserve (or look up) a prefix for an engine. Throws synchronously if the\n * same prefix is already owned by a different engine.\n */\nexport function reservePrefix(engine: string, prefix: string): string {\n if (!prefix.startsWith('$') || !prefix.endsWith('.')) {\n throw new Error(\n `[skygraph] engine \"${engine}\" tried to reserve invalid prefix \"${prefix}\". ` +\n `Engine prefixes must start with \"$\" and end with \".\".`,\n )\n }\n\n const existing = reserved.get(prefix)\n if (existing !== undefined && existing !== engine) {\n throw new Error(\n `[skygraph] prefix \"${prefix}\" is already owned by engine \"${existing}\", ` +\n `cannot reserve it for \"${engine}\". ` +\n `Pick a different prefix or update the registry.`,\n )\n }\n\n reserved.set(prefix, engine)\n return prefix\n}\n\n/** Snapshot of registered engine prefixes (debug / introspection). */\nexport function listReservedPrefixes(): ReadonlyMap<string, string> {\n return new Map(reserved)\n}\n\n/** Test-only helper. DO NOT call from engine code. */\nexport function __resetPrefixes(): void {\n reserved.clear()\n}\n\n// ── Canonical in-tree reservations ─────────────────────────────────────────\n//\n// Listed here so they are reserved even if the engine module is not loaded.\n// New engines must add their entry here and reference it from their\n// implementation via `reservePrefix(...)`.\n\nexport const FORM_META_PREFIX = reservePrefix('form', '$meta.')\nexport const TABLE_PREFIX = reservePrefix('table', '$table.')\nexport const TREE_PREFIX = reservePrefix('tree', '$tree.')\nexport const GRAPH_PREFIX = reservePrefix('graph', '$graph.')\nexport const CALENDAR_PREFIX = reservePrefix('calendar', '$calendar.')\n","import type { Core } from '../../types'\nimport type {\n TreeKey,\n TreeNodeData,\n TreeFieldNames,\n TreeEngine,\n TreeEngineOptions,\n TreeState,\n} from './types'\n\nimport { TREE_PREFIX } from '../namespaces'\n\nlet treeCounter = 0\n\ninterface InternalNode {\n node: TreeNodeData\n key: TreeKey\n depth: number\n parentKey: TreeKey | null\n childKeys: TreeKey[]\n isLeaf: boolean\n}\n\nexport function createTree(core: Core, options?: TreeEngineOptions): TreeEngine {\n const treeId = `tr${treeCounter++}`\n const fn: Required<TreeFieldNames> = {\n key: options?.fieldNames?.key ?? 'key',\n title: options?.fieldNames?.title ?? 'title',\n children: options?.fieldNames?.children ?? 'children',\n }\n const checkStrictly = options?.checkStrictly ?? false\n\n let nodes: TreeNodeData[] = []\n let nodeMap = new Map<TreeKey, InternalNode>()\n\n let expandedKeys = new Set<TreeKey>(options?.defaultExpandedKeys ?? [])\n let checkedKeys = new Set<TreeKey>(options?.defaultCheckedKeys ?? [])\n let halfCheckedKeys = new Set<TreeKey>()\n let selectedKeys = new Set<TreeKey>(options?.defaultSelectedKeys ?? [])\n let loadedKeys = new Set<TreeKey>()\n let loadingKeys = new Set<TreeKey>()\n let filteredKeys: Set<TreeKey> | null = null\n\n function getKey(node: TreeNodeData): TreeKey {\n return (node as Record<string, unknown>)[fn.key] as TreeKey\n }\n\n function getChildren(node: TreeNodeData): TreeNodeData[] | undefined {\n return (node as Record<string, unknown>)[fn.children] as TreeNodeData[] | undefined\n }\n\n function buildMap(data: TreeNodeData[]): void {\n nodeMap = new Map()\n function walk(list: TreeNodeData[], parentKey: TreeKey | null, depth: number) {\n for (const n of list) {\n const k = getKey(n)\n const children = getChildren(n)\n const childKeys: TreeKey[] = []\n const isLeaf = n.isLeaf === true || (!children || children.length === 0)\n\n if (children && children.length > 0) {\n for (const ch of children) childKeys.push(getKey(ch))\n }\n\n nodeMap.set(k, { node: n, key: k, depth, parentKey, childKeys, isLeaf })\n\n if (children && children.length > 0) {\n walk(children, k, depth + 1)\n }\n }\n }\n walk(data, null, 0)\n }\n\n function getAllDescendantKeys(key: TreeKey): TreeKey[] {\n const result: TreeKey[] = []\n const internal = nodeMap.get(key)\n if (!internal) return result\n for (const ck of internal.childKeys) {\n result.push(ck)\n result.push(...getAllDescendantKeys(ck))\n }\n return result\n }\n\n function getAllAncestorKeys(key: TreeKey): TreeKey[] {\n const result: TreeKey[] = []\n let current = nodeMap.get(key)\n while (current && current.parentKey !== null) {\n result.push(current.parentKey)\n current = nodeMap.get(current.parentKey)\n }\n return result\n }\n\n function recomputeHalfChecked(): void {\n if (checkStrictly) {\n halfCheckedKeys = new Set()\n return\n }\n const half = new Set<TreeKey>()\n for (const [key, internal] of nodeMap) {\n if (internal.childKeys.length === 0) continue\n if (checkedKeys.has(key)) continue\n const allDescendants = getAllDescendantKeys(key)\n const someChecked = allDescendants.some((dk) => checkedKeys.has(dk))\n if (someChecked) half.add(key)\n }\n halfCheckedKeys = half\n }\n\n function cascadeCheck(key: TreeKey, checked: boolean): void {\n if (checkStrictly) {\n if (checked) checkedKeys.add(key)\n else checkedKeys.delete(key)\n return\n }\n\n const internal = nodeMap.get(key)\n if (!internal) return\n\n if (checked) {\n checkedKeys.add(key)\n const descendants = getAllDescendantKeys(key)\n for (const dk of descendants) {\n const dn = nodeMap.get(dk)\n if (dn && !dn.node.disabled && !dn.node.disableCheckbox) {\n checkedKeys.add(dk)\n }\n }\n } else {\n checkedKeys.delete(key)\n const descendants = getAllDescendantKeys(key)\n for (const dk of descendants) {\n checkedKeys.delete(dk)\n }\n }\n\n // Propagate up\n const ancestors = getAllAncestorKeys(key)\n for (const ak of ancestors) {\n const an = nodeMap.get(ak)\n if (!an) continue\n const allChildren = getAllDescendantKeys(ak)\n const enabledChildren = allChildren.filter((ck) => {\n const cn = nodeMap.get(ck)\n return cn && !cn.node.disabled && !cn.node.disableCheckbox\n })\n const allChecked = enabledChildren.length > 0 && enabledChildren.every((ck) => checkedKeys.has(ck))\n if (allChecked) {\n checkedKeys.add(ak)\n } else {\n checkedKeys.delete(ak)\n }\n }\n\n recomputeHalfChecked()\n }\n\n function removeFromTree(data: TreeNodeData[], targetKey: TreeKey): { remaining: TreeNodeData[]; removed: TreeNodeData | null } {\n let removed: TreeNodeData | null = null\n const remaining = data.filter((n) => {\n if (getKey(n) === targetKey) {\n removed = n\n return false\n }\n return true\n })\n if (!removed) {\n for (const n of remaining) {\n const children = getChildren(n)\n if (children && children.length > 0) {\n const result = removeFromTree(children, targetKey)\n if (result.removed) {\n ;(n as Record<string, unknown>)[fn.children] = result.remaining\n return { remaining, removed: result.removed }\n }\n }\n }\n }\n return { remaining, removed }\n }\n\n function insertIntoTree(\n data: TreeNodeData[],\n targetKey: TreeKey,\n node: TreeNodeData,\n position: -1 | 0 | 1,\n ): TreeNodeData[] {\n if (position === 0) {\n return data.map((n) => {\n if (getKey(n) === targetKey) {\n const children = getChildren(n) ?? []\n ;(n as Record<string, unknown>)[fn.children] = [...children, node]\n return n\n }\n const children = getChildren(n)\n if (children && children.length > 0) {\n ;(n as Record<string, unknown>)[fn.children] = insertIntoTree(children, targetKey, node, position)\n }\n return n\n })\n }\n\n const result: TreeNodeData[] = []\n for (const n of data) {\n if (getKey(n) === targetKey) {\n if (position === -1) {\n result.push(node, n)\n } else {\n result.push(n, node)\n }\n } else {\n const children = getChildren(n)\n if (children && children.length > 0) {\n ;(n as Record<string, unknown>)[fn.children] = insertIntoTree(children, targetKey, node, position)\n }\n result.push(n)\n }\n }\n return result\n }\n\n function publishState(): void {\n const path = (k: string) => `${TREE_PREFIX}${treeId}.${k}`\n core.batch(() => {\n core.set(path('expandedKeys'), [...expandedKeys])\n core.set(path('checkedKeys'), [...checkedKeys])\n core.set(path('halfCheckedKeys'), [...halfCheckedKeys])\n core.set(path('selectedKeys'), [...selectedKeys])\n core.set(path('loadedKeys'), [...loadedKeys])\n core.set(path('loadingKeys'), [...loadingKeys])\n })\n }\n\n function initDefaults(): void {\n if (options?.defaultExpandAll) {\n for (const [key, internal] of nodeMap) {\n if (!internal.isLeaf) expandedKeys.add(key)\n }\n }\n\n if (options?.defaultCheckedKeys && !checkStrictly) {\n for (const key of options.defaultCheckedKeys) {\n cascadeCheck(key, true)\n }\n }\n\n recomputeHalfChecked()\n publishState()\n }\n\n const engine: TreeEngine = {\n setData(data: TreeNodeData[]): void {\n nodes = data\n buildMap(data)\n initDefaults()\n },\n\n getData(): TreeNodeData[] {\n return nodes\n },\n\n // Expand\n expand(key: TreeKey): void {\n expandedKeys.add(key)\n publishState()\n },\n\n collapse(key: TreeKey): void {\n expandedKeys.delete(key)\n publishState()\n },\n\n toggleExpand(key: TreeKey): void {\n if (expandedKeys.has(key)) expandedKeys.delete(key)\n else expandedKeys.add(key)\n publishState()\n },\n\n setExpandedKeys(keys: TreeKey[]): void {\n expandedKeys = new Set(keys)\n publishState()\n },\n\n expandAll(): void {\n for (const [key, internal] of nodeMap) {\n if (!internal.isLeaf) expandedKeys.add(key)\n }\n publishState()\n },\n\n collapseAll(): void {\n expandedKeys = new Set()\n publishState()\n },\n\n // Check\n check(key: TreeKey): void {\n cascadeCheck(key, true)\n publishState()\n },\n\n uncheck(key: TreeKey): void {\n cascadeCheck(key, false)\n publishState()\n },\n\n toggleCheck(key: TreeKey): void {\n const checked = !checkedKeys.has(key)\n cascadeCheck(key, checked)\n publishState()\n },\n\n setCheckedKeys(keys: TreeKey[]): void {\n checkedKeys = new Set(keys)\n recomputeHalfChecked()\n publishState()\n },\n\n // Select\n select(key: TreeKey, multiple?: boolean): void {\n if (multiple) {\n if (selectedKeys.has(key)) selectedKeys.delete(key)\n else selectedKeys.add(key)\n } else {\n selectedKeys = new Set([key])\n }\n publishState()\n },\n\n deselect(key: TreeKey): void {\n selectedKeys.delete(key)\n publishState()\n },\n\n setSelectedKeys(keys: TreeKey[]): void {\n selectedKeys = new Set(keys)\n publishState()\n },\n\n // Async loading\n markLoading(key: TreeKey): void {\n loadingKeys.add(key)\n publishState()\n },\n\n markLoaded(key: TreeKey): void {\n loadingKeys.delete(key)\n loadedKeys.add(key)\n publishState()\n },\n\n addChildren(parentKey: TreeKey, children: TreeNodeData[]): void {\n const internal = nodeMap.get(parentKey)\n if (!internal) return\n ;(internal.node as Record<string, unknown>)[fn.children] = children\n buildMap(nodes)\n publishState()\n },\n\n // Drag & drop\n moveNode(dragKey: TreeKey, dropKey: TreeKey, position: -1 | 0 | 1): TreeNodeData[] | null {\n if (dragKey === dropKey) return null\n const ancestors = getAllAncestorKeys(dropKey)\n if (ancestors.includes(dragKey)) return null\n\n const { remaining, removed } = removeFromTree(nodes, dragKey)\n if (!removed) return null\n\n nodes = insertIntoTree(remaining, dropKey, removed, position)\n buildMap(nodes)\n publishState()\n return nodes\n },\n\n // Filter\n filterNodes(predicate: ((node: TreeNodeData) => boolean) | null): void {\n if (!predicate) {\n filteredKeys = null\n return\n }\n const matched = new Set<TreeKey>()\n for (const [key, internal] of nodeMap) {\n if (predicate(internal.node)) {\n matched.add(key)\n const ancestors = getAllAncestorKeys(key)\n for (const ak of ancestors) matched.add(ak)\n }\n }\n filteredKeys = matched\n },\n\n getFilteredKeys(): TreeKey[] | null {\n return filteredKeys ? [...filteredKeys] : null\n },\n\n // Query\n getNode(key: TreeKey): TreeNodeData | undefined {\n return nodeMap.get(key)?.node\n },\n\n getParentKey(key: TreeKey): TreeKey | null {\n return nodeMap.get(key)?.parentKey ?? null\n },\n\n getChildKeys(key: TreeKey): TreeKey[] {\n return nodeMap.get(key)?.childKeys ?? []\n },\n\n getAllKeys(): TreeKey[] {\n return [...nodeMap.keys()]\n },\n\n getLeafKeys(): TreeKey[] {\n const result: TreeKey[] = []\n for (const [key, internal] of nodeMap) {\n if (internal.isLeaf) result.push(key)\n }\n return result\n },\n\n getState(): TreeState {\n return {\n expandedKeys: [...expandedKeys],\n checkedKeys: [...checkedKeys],\n halfCheckedKeys: [...halfCheckedKeys],\n selectedKeys: [...selectedKeys],\n loadedKeys: [...loadedKeys],\n loadingKeys: [...loadingKeys],\n }\n },\n\n getFlatNodes() {\n const result: Array<{ node: TreeNodeData; key: TreeKey; depth: number; isLeaf: boolean; parentKey: TreeKey | null }> = []\n\n function walk(list: TreeNodeData[], parentKey: TreeKey | null, depth: number) {\n for (const n of list) {\n const k = getKey(n)\n if (filteredKeys && !filteredKeys.has(k)) continue\n\n const children = getChildren(n)\n const isLeaf = n.isLeaf === true || (!children || children.length === 0)\n\n result.push({ node: n, key: k, depth, isLeaf, parentKey })\n\n if (!isLeaf && expandedKeys.has(k) && children && children.length > 0) {\n walk(children, k, depth + 1)\n }\n }\n }\n\n walk(nodes, null, 0)\n return result\n },\n\n reset(): void {\n nodes = []\n nodeMap = new Map()\n expandedKeys = new Set()\n checkedKeys = new Set()\n halfCheckedKeys = new Set()\n selectedKeys = new Set()\n loadedKeys = new Set()\n loadingKeys = new Set()\n filteredKeys = null\n publishState()\n },\n }\n\n return engine\n}\n"],"mappings":"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","names":["children"]}
1
+ {"version":3,"sources":["../src/engines/tree/index.ts","../src/engines/namespaces.ts","../src/engines/tree/TreeEngine.ts"],"sourcesContent":["export { createTree } from './TreeEngine'\r\nexport type {\r\n TreeEngine,\r\n TreeState,\r\n TreeEngineOptions,\r\n TreeKey,\r\n TreeNodeData,\r\n TreeFieldNames,\r\n} from './types'\r\n","/**\n * Engine path namespace registry\n *\n * Every engine that stores state in the Core store under a `$`-prefixed\n * namespace MUST register its prefix here. This prevents two engines from\n * silently colliding on the same root path (e.g. two engines both writing\n * under `$meta.`).\n *\n * Rules:\n * 1. Prefixes MUST start with `$` and end with `.`.\n * 2. Prefixes MUST be unique across all engines.\n * 3. An engine MUST use `reservedPrefix(name)` at module load so\n * collisions surface at import time during dev/test.\n * 4. User data MUST NOT live under `$` prefixes — reserve them for engines.\n */\n\nconst reserved = new Map<string, string>()\n\n/**\n * Reserve (or look up) a prefix for an engine. Throws synchronously if the\n * same prefix is already owned by a different engine.\n */\nexport function reservePrefix(engine: string, prefix: string): string {\n if (!prefix.startsWith('$') || !prefix.endsWith('.')) {\n throw new Error(\n `[skygraph] engine \"${engine}\" tried to reserve invalid prefix \"${prefix}\". ` +\n `Engine prefixes must start with \"$\" and end with \".\".`,\n )\n }\n\n const existing = reserved.get(prefix)\n if (existing !== undefined && existing !== engine) {\n throw new Error(\n `[skygraph] prefix \"${prefix}\" is already owned by engine \"${existing}\", ` +\n `cannot reserve it for \"${engine}\". ` +\n `Pick a different prefix or update the registry.`,\n )\n }\n\n reserved.set(prefix, engine)\n return prefix\n}\n\n/** Snapshot of registered engine prefixes (debug / introspection). */\nexport function listReservedPrefixes(): ReadonlyMap<string, string> {\n return new Map(reserved)\n}\n\n/** Test-only helper. DO NOT call from engine code. */\nexport function __resetPrefixes(): void {\n reserved.clear()\n}\n\n// ── Canonical in-tree reservations ─────────────────────────────────────────\n//\n// Listed here so they are reserved even if the engine module is not loaded.\n// New engines must add their entry here and reference it from their\n// implementation via `reservePrefix(...)`.\n\nexport const FORM_META_PREFIX = reservePrefix('form', '$meta.')\nexport const TABLE_PREFIX = reservePrefix('table', '$table.')\nexport const TREE_PREFIX = reservePrefix('tree', '$tree.')\nexport const GRAPH_PREFIX = reservePrefix('graph', '$graph.')\nexport const CALENDAR_PREFIX = reservePrefix('calendar', '$calendar.')\n","import type { Core } from '../../types'\r\nimport type {\r\n TreeKey,\r\n TreeNodeData,\r\n TreeFieldNames,\r\n TreeEngine,\r\n TreeEngineOptions,\r\n TreeState,\r\n} from './types'\r\n\r\nimport { TREE_PREFIX } from '../namespaces'\r\n\r\nlet treeCounter = 0\r\n\r\ninterface InternalNode {\r\n node: TreeNodeData\r\n key: TreeKey\r\n depth: number\r\n parentKey: TreeKey | null\r\n childKeys: TreeKey[]\r\n isLeaf: boolean\r\n}\r\n\r\nexport function createTree(core: Core, options?: TreeEngineOptions): TreeEngine {\r\n const treeId = `tr${treeCounter++}`\r\n const fn: Required<TreeFieldNames> = {\r\n key: options?.fieldNames?.key ?? 'key',\r\n title: options?.fieldNames?.title ?? 'title',\r\n children: options?.fieldNames?.children ?? 'children',\r\n }\r\n const checkStrictly = options?.checkStrictly ?? false\r\n\r\n let nodes: TreeNodeData[] = []\r\n let nodeMap = new Map<TreeKey, InternalNode>()\r\n\r\n let expandedKeys = new Set<TreeKey>(options?.defaultExpandedKeys ?? [])\r\n let checkedKeys = new Set<TreeKey>(options?.defaultCheckedKeys ?? [])\r\n let halfCheckedKeys = new Set<TreeKey>()\r\n let selectedKeys = new Set<TreeKey>(options?.defaultSelectedKeys ?? [])\r\n let loadedKeys = new Set<TreeKey>()\r\n let loadingKeys = new Set<TreeKey>()\r\n let filteredKeys: Set<TreeKey> | null = null\r\n\r\n function getKey(node: TreeNodeData): TreeKey {\r\n return (node as Record<string, unknown>)[fn.key] as TreeKey\r\n }\r\n\r\n function getChildren(node: TreeNodeData): TreeNodeData[] | undefined {\r\n return (node as Record<string, unknown>)[fn.children] as TreeNodeData[] | undefined\r\n }\r\n\r\n function buildMap(data: TreeNodeData[]): void {\r\n nodeMap = new Map()\r\n function walk(list: TreeNodeData[], parentKey: TreeKey | null, depth: number) {\r\n for (const n of list) {\r\n const k = getKey(n)\r\n const children = getChildren(n)\r\n const childKeys: TreeKey[] = []\r\n const isLeaf = n.isLeaf === true || (!children || children.length === 0)\r\n\r\n if (children && children.length > 0) {\r\n for (const ch of children) childKeys.push(getKey(ch))\r\n }\r\n\r\n nodeMap.set(k, { node: n, key: k, depth, parentKey, childKeys, isLeaf })\r\n\r\n if (children && children.length > 0) {\r\n walk(children, k, depth + 1)\r\n }\r\n }\r\n }\r\n walk(data, null, 0)\r\n }\r\n\r\n function getAllDescendantKeys(key: TreeKey): TreeKey[] {\r\n const result: TreeKey[] = []\r\n const internal = nodeMap.get(key)\r\n if (!internal) return result\r\n for (const ck of internal.childKeys) {\r\n result.push(ck)\r\n result.push(...getAllDescendantKeys(ck))\r\n }\r\n return result\r\n }\r\n\r\n function getAllAncestorKeys(key: TreeKey): TreeKey[] {\r\n const result: TreeKey[] = []\r\n let current = nodeMap.get(key)\r\n while (current && current.parentKey !== null) {\r\n result.push(current.parentKey)\r\n current = nodeMap.get(current.parentKey)\r\n }\r\n return result\r\n }\r\n\r\n function recomputeHalfChecked(): void {\r\n if (checkStrictly) {\r\n halfCheckedKeys = new Set()\r\n return\r\n }\r\n const half = new Set<TreeKey>()\r\n for (const [key, internal] of nodeMap) {\r\n if (internal.childKeys.length === 0) continue\r\n if (checkedKeys.has(key)) continue\r\n const allDescendants = getAllDescendantKeys(key)\r\n const someChecked = allDescendants.some((dk) => checkedKeys.has(dk))\r\n if (someChecked) half.add(key)\r\n }\r\n halfCheckedKeys = half\r\n }\r\n\r\n function cascadeCheck(key: TreeKey, checked: boolean): void {\r\n if (checkStrictly) {\r\n if (checked) checkedKeys.add(key)\r\n else checkedKeys.delete(key)\r\n return\r\n }\r\n\r\n const internal = nodeMap.get(key)\r\n if (!internal) return\r\n\r\n if (checked) {\r\n checkedKeys.add(key)\r\n const descendants = getAllDescendantKeys(key)\r\n for (const dk of descendants) {\r\n const dn = nodeMap.get(dk)\r\n if (dn && !dn.node.disabled && !dn.node.disableCheckbox) {\r\n checkedKeys.add(dk)\r\n }\r\n }\r\n } else {\r\n checkedKeys.delete(key)\r\n const descendants = getAllDescendantKeys(key)\r\n for (const dk of descendants) {\r\n checkedKeys.delete(dk)\r\n }\r\n }\r\n\r\n // Propagate up\r\n const ancestors = getAllAncestorKeys(key)\r\n for (const ak of ancestors) {\r\n const an = nodeMap.get(ak)\r\n if (!an) continue\r\n const allChildren = getAllDescendantKeys(ak)\r\n const enabledChildren = allChildren.filter((ck) => {\r\n const cn = nodeMap.get(ck)\r\n return cn && !cn.node.disabled && !cn.node.disableCheckbox\r\n })\r\n const allChecked = enabledChildren.length > 0 && enabledChildren.every((ck) => checkedKeys.has(ck))\r\n if (allChecked) {\r\n checkedKeys.add(ak)\r\n } else {\r\n checkedKeys.delete(ak)\r\n }\r\n }\r\n\r\n recomputeHalfChecked()\r\n }\r\n\r\n function removeFromTree(data: TreeNodeData[], targetKey: TreeKey): { remaining: TreeNodeData[]; removed: TreeNodeData | null } {\r\n let removed: TreeNodeData | null = null\r\n const remaining = data.filter((n) => {\r\n if (getKey(n) === targetKey) {\r\n removed = n\r\n return false\r\n }\r\n return true\r\n })\r\n if (!removed) {\r\n for (const n of remaining) {\r\n const children = getChildren(n)\r\n if (children && children.length > 0) {\r\n const result = removeFromTree(children, targetKey)\r\n if (result.removed) {\r\n ;(n as Record<string, unknown>)[fn.children] = result.remaining\r\n return { remaining, removed: result.removed }\r\n }\r\n }\r\n }\r\n }\r\n return { remaining, removed }\r\n }\r\n\r\n function insertIntoTree(\r\n data: TreeNodeData[],\r\n targetKey: TreeKey,\r\n node: TreeNodeData,\r\n position: -1 | 0 | 1,\r\n ): TreeNodeData[] {\r\n if (position === 0) {\r\n return data.map((n) => {\r\n if (getKey(n) === targetKey) {\r\n const children = getChildren(n) ?? []\r\n ;(n as Record<string, unknown>)[fn.children] = [...children, node]\r\n return n\r\n }\r\n const children = getChildren(n)\r\n if (children && children.length > 0) {\r\n ;(n as Record<string, unknown>)[fn.children] = insertIntoTree(children, targetKey, node, position)\r\n }\r\n return n\r\n })\r\n }\r\n\r\n const result: TreeNodeData[] = []\r\n for (const n of data) {\r\n if (getKey(n) === targetKey) {\r\n if (position === -1) {\r\n result.push(node, n)\r\n } else {\r\n result.push(n, node)\r\n }\r\n } else {\r\n const children = getChildren(n)\r\n if (children && children.length > 0) {\r\n ;(n as Record<string, unknown>)[fn.children] = insertIntoTree(children, targetKey, node, position)\r\n }\r\n result.push(n)\r\n }\r\n }\r\n return result\r\n }\r\n\r\n function publishState(): void {\r\n const path = (k: string) => `${TREE_PREFIX}${treeId}.${k}`\r\n core.batch(() => {\r\n core.set(path('expandedKeys'), [...expandedKeys])\r\n core.set(path('checkedKeys'), [...checkedKeys])\r\n core.set(path('halfCheckedKeys'), [...halfCheckedKeys])\r\n core.set(path('selectedKeys'), [...selectedKeys])\r\n core.set(path('loadedKeys'), [...loadedKeys])\r\n core.set(path('loadingKeys'), [...loadingKeys])\r\n })\r\n }\r\n\r\n function initDefaults(): void {\r\n if (options?.defaultExpandAll) {\r\n for (const [key, internal] of nodeMap) {\r\n if (!internal.isLeaf) expandedKeys.add(key)\r\n }\r\n }\r\n\r\n if (options?.defaultCheckedKeys && !checkStrictly) {\r\n for (const key of options.defaultCheckedKeys) {\r\n cascadeCheck(key, true)\r\n }\r\n }\r\n\r\n recomputeHalfChecked()\r\n publishState()\r\n }\r\n\r\n const engine: TreeEngine = {\r\n setData(data: TreeNodeData[]): void {\r\n nodes = data\r\n buildMap(data)\r\n initDefaults()\r\n },\r\n\r\n getData(): TreeNodeData[] {\r\n return nodes\r\n },\r\n\r\n // Expand\r\n expand(key: TreeKey): void {\r\n expandedKeys.add(key)\r\n publishState()\r\n },\r\n\r\n collapse(key: TreeKey): void {\r\n expandedKeys.delete(key)\r\n publishState()\r\n },\r\n\r\n toggleExpand(key: TreeKey): void {\r\n if (expandedKeys.has(key)) expandedKeys.delete(key)\r\n else expandedKeys.add(key)\r\n publishState()\r\n },\r\n\r\n setExpandedKeys(keys: TreeKey[]): void {\r\n expandedKeys = new Set(keys)\r\n publishState()\r\n },\r\n\r\n expandAll(): void {\r\n for (const [key, internal] of nodeMap) {\r\n if (!internal.isLeaf) expandedKeys.add(key)\r\n }\r\n publishState()\r\n },\r\n\r\n collapseAll(): void {\r\n expandedKeys = new Set()\r\n publishState()\r\n },\r\n\r\n // Check\r\n check(key: TreeKey): void {\r\n cascadeCheck(key, true)\r\n publishState()\r\n },\r\n\r\n uncheck(key: TreeKey): void {\r\n cascadeCheck(key, false)\r\n publishState()\r\n },\r\n\r\n toggleCheck(key: TreeKey): void {\r\n const checked = !checkedKeys.has(key)\r\n cascadeCheck(key, checked)\r\n publishState()\r\n },\r\n\r\n setCheckedKeys(keys: TreeKey[]): void {\r\n checkedKeys = new Set(keys)\r\n recomputeHalfChecked()\r\n publishState()\r\n },\r\n\r\n // Select\r\n select(key: TreeKey, multiple?: boolean): void {\r\n if (multiple) {\r\n if (selectedKeys.has(key)) selectedKeys.delete(key)\r\n else selectedKeys.add(key)\r\n } else {\r\n selectedKeys = new Set([key])\r\n }\r\n publishState()\r\n },\r\n\r\n deselect(key: TreeKey): void {\r\n selectedKeys.delete(key)\r\n publishState()\r\n },\r\n\r\n setSelectedKeys(keys: TreeKey[]): void {\r\n selectedKeys = new Set(keys)\r\n publishState()\r\n },\r\n\r\n // Async loading\r\n markLoading(key: TreeKey): void {\r\n loadingKeys.add(key)\r\n publishState()\r\n },\r\n\r\n markLoaded(key: TreeKey): void {\r\n loadingKeys.delete(key)\r\n loadedKeys.add(key)\r\n publishState()\r\n },\r\n\r\n addChildren(parentKey: TreeKey, children: TreeNodeData[]): void {\r\n const internal = nodeMap.get(parentKey)\r\n if (!internal) return\r\n ;(internal.node as Record<string, unknown>)[fn.children] = children\r\n buildMap(nodes)\r\n publishState()\r\n },\r\n\r\n // Drag & drop\r\n moveNode(dragKey: TreeKey, dropKey: TreeKey, position: -1 | 0 | 1): TreeNodeData[] | null {\r\n if (dragKey === dropKey) return null\r\n const ancestors = getAllAncestorKeys(dropKey)\r\n if (ancestors.includes(dragKey)) return null\r\n\r\n const { remaining, removed } = removeFromTree(nodes, dragKey)\r\n if (!removed) return null\r\n\r\n nodes = insertIntoTree(remaining, dropKey, removed, position)\r\n buildMap(nodes)\r\n publishState()\r\n return nodes\r\n },\r\n\r\n // Filter\r\n filterNodes(predicate: ((node: TreeNodeData) => boolean) | null): void {\r\n if (!predicate) {\r\n filteredKeys = null\r\n return\r\n }\r\n const matched = new Set<TreeKey>()\r\n for (const [key, internal] of nodeMap) {\r\n if (predicate(internal.node)) {\r\n matched.add(key)\r\n const ancestors = getAllAncestorKeys(key)\r\n for (const ak of ancestors) matched.add(ak)\r\n }\r\n }\r\n filteredKeys = matched\r\n },\r\n\r\n getFilteredKeys(): TreeKey[] | null {\r\n return filteredKeys ? [...filteredKeys] : null\r\n },\r\n\r\n // Query\r\n getNode(key: TreeKey): TreeNodeData | undefined {\r\n return nodeMap.get(key)?.node\r\n },\r\n\r\n getParentKey(key: TreeKey): TreeKey | null {\r\n return nodeMap.get(key)?.parentKey ?? null\r\n },\r\n\r\n getChildKeys(key: TreeKey): TreeKey[] {\r\n return nodeMap.get(key)?.childKeys ?? []\r\n },\r\n\r\n getAllKeys(): TreeKey[] {\r\n return [...nodeMap.keys()]\r\n },\r\n\r\n getLeafKeys(): TreeKey[] {\r\n const result: TreeKey[] = []\r\n for (const [key, internal] of nodeMap) {\r\n if (internal.isLeaf) result.push(key)\r\n }\r\n return result\r\n },\r\n\r\n getState(): TreeState {\r\n return {\r\n expandedKeys: [...expandedKeys],\r\n checkedKeys: [...checkedKeys],\r\n halfCheckedKeys: [...halfCheckedKeys],\r\n selectedKeys: [...selectedKeys],\r\n loadedKeys: [...loadedKeys],\r\n loadingKeys: [...loadingKeys],\r\n }\r\n },\r\n\r\n getFlatNodes() {\r\n const result: Array<{ node: TreeNodeData; key: TreeKey; depth: number; isLeaf: boolean; parentKey: TreeKey | null }> = []\r\n\r\n function walk(list: TreeNodeData[], parentKey: TreeKey | null, depth: number) {\r\n for (const n of list) {\r\n const k = getKey(n)\r\n if (filteredKeys && !filteredKeys.has(k)) continue\r\n\r\n const children = getChildren(n)\r\n const isLeaf = n.isLeaf === true || (!children || children.length === 0)\r\n\r\n result.push({ node: n, key: k, depth, isLeaf, parentKey })\r\n\r\n if (!isLeaf && expandedKeys.has(k) && children && children.length > 0) {\r\n walk(children, k, depth + 1)\r\n }\r\n }\r\n }\r\n\r\n walk(nodes, null, 0)\r\n return result\r\n },\r\n\r\n reset(): void {\r\n nodes = []\r\n nodeMap = new Map()\r\n expandedKeys = new Set()\r\n checkedKeys = new Set()\r\n halfCheckedKeys = new Set()\r\n selectedKeys = new Set()\r\n loadedKeys = new Set()\r\n loadingKeys = new Set()\r\n filteredKeys = null\r\n publishState()\r\n },\r\n }\r\n\r\n return engine\r\n}\r\n"],"mappings":"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","names":["children"]}