@skygraph/core 0.0.0-placeholder.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +42 -6
- package/dist/calendar.cjs +404 -0
- package/dist/calendar.cjs.map +1 -0
- package/dist/calendar.d.cts +209 -0
- package/dist/calendar.d.ts +209 -0
- package/dist/calendar.js +12 -0
- package/dist/calendar.js.map +1 -0
- package/dist/chunk-5CCD5Q4B.js +36 -0
- package/dist/chunk-5CCD5Q4B.js.map +1 -0
- package/dist/chunk-7FA2TBSZ.js +1211 -0
- package/dist/chunk-7FA2TBSZ.js.map +1 -0
- package/dist/chunk-AB3RLBLW.js +357 -0
- package/dist/chunk-AB3RLBLW.js.map +1 -0
- package/dist/chunk-GEMALROQ.js +258 -0
- package/dist/chunk-GEMALROQ.js.map +1 -0
- package/dist/chunk-IY6LJU3L.js +256 -0
- package/dist/chunk-IY6LJU3L.js.map +1 -0
- package/dist/chunk-KMGRNPLG.js +453 -0
- package/dist/chunk-KMGRNPLG.js.map +1 -0
- package/dist/chunk-Y7FTBBYX.js +397 -0
- package/dist/chunk-Y7FTBBYX.js.map +1 -0
- package/dist/chunk-ZWB7JGAJ.js +488 -0
- package/dist/chunk-ZWB7JGAJ.js.map +1 -0
- package/dist/form.cjs +498 -0
- package/dist/form.cjs.map +1 -0
- package/dist/form.d.cts +130 -0
- package/dist/form.d.ts +130 -0
- package/dist/form.js +8 -0
- package/dist/form.js.map +1 -0
- package/dist/graph.cjs +1269 -0
- package/dist/graph.cjs.map +1 -0
- package/dist/graph.d.cts +671 -0
- package/dist/graph.d.ts +671 -0
- package/dist/graph.js +34 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.cjs +3860 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +144 -0
- package/dist/index.d.ts +144 -0
- package/dist/index.js +477 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime-internal.cjs +339 -0
- package/dist/runtime-internal.cjs.map +1 -0
- package/dist/runtime-internal.d.cts +130 -0
- package/dist/runtime-internal.d.ts +130 -0
- package/dist/runtime-internal.js +67 -0
- package/dist/runtime-internal.js.map +1 -0
- package/dist/table.cjs +537 -0
- package/dist/table.cjs.map +1 -0
- package/dist/table.d.cts +215 -0
- package/dist/table.d.ts +215 -0
- package/dist/table.js +16 -0
- package/dist/table.js.map +1 -0
- package/dist/tree.cjs +442 -0
- package/dist/tree.cjs.map +1 -0
- package/dist/tree.d.cts +76 -0
- package/dist/tree.d.ts +76 -0
- package/dist/tree.js +8 -0
- package/dist/tree.js.map +1 -0
- package/dist/types-B-rKQJ4R.d.cts +35 -0
- package/dist/types-B-rKQJ4R.d.ts +35 -0
- package/dist/virtual.cjs +283 -0
- package/dist/virtual.cjs.map +1 -0
- package/dist/virtual.d.cts +96 -0
- package/dist/virtual.d.ts +96 -0
- package/dist/virtual.js +9 -0
- package/dist/virtual.js.map +1 -0
- package/package.json +98 -18
- package/index.js +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/engines/calendar/availability.ts","../src/engines/calendar/conflicts.ts","../src/engines/calendar/CalendarEngine.ts"],"sourcesContent":["import type { AvailabilityRule, DayOfWeek, Resource } from './types'\n\nconst DAY_MS = 86_400_000\n\n/**\n * Parse `HH:MM` into milliseconds-from-midnight.\n *\n * Returns `null` for malformed input — callers treat that as \"rule has no\n * window on this side\" (i.e. the canonical 00:00 / 24:00 fallback).\n */\nfunction parseHHMM(s: string | undefined, fallback: number): number {\n if (s === undefined) return fallback\n const m = /^(\\d{1,2}):(\\d{2})$/.exec(s)\n if (!m) return fallback\n const h = Number(m[1])\n const mm = Number(m[2])\n if (!Number.isFinite(h) || !Number.isFinite(mm)) return fallback\n if (h < 0 || h > 24 || mm < 0 || mm >= 60) return fallback\n return h * 3_600_000 + mm * 60_000\n}\n\n/** Rules attached to one resource. Cheap to compute, cached per call. */\nfunction rulesFor(\n resourceId: string,\n rules: readonly AvailabilityRule[],\n): AvailabilityRule[] {\n return rules.filter((r) => r.resourceId === resourceId)\n}\n\nfunction dayBucket(t: number): { dayStart: number; dayOfWeek: DayOfWeek } {\n // Floor to UTC day boundary; getUTCDay returns 0..6 (Sun..Sat).\n const d = new Date(t)\n const dayStart =\n Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())\n const dayOfWeek = new Date(dayStart).getUTCDay() as DayOfWeek\n return { dayStart, dayOfWeek }\n}\n\n/**\n * `true` ⇔ the entire `[start, end)` interval falls inside at least one\n * availability window for `resource`. A resource without any matching\n * rule (or without any rules at all) is considered always available —\n * unconfigured calendars stay friendly.\n *\n * Implementation: walk one UTC day at a time and check that the slice of\n * `[start, end)` intersected with that day is fully covered by the union\n * of windows attached to that day. The union is built by sorting rule\n * windows by `from` and merging overlaps in O(k log k) where k = rules\n * for that day (typically 1–3).\n */\nexport function isAvailable(\n resource: Resource,\n range: { start: number; end: number },\n rules: readonly AvailabilityRule[],\n): boolean {\n if (range.end <= range.start) return true\n\n const own = rulesFor(resource.id, rules)\n if (own.length === 0) return true\n\n let cursor = range.start\n while (cursor < range.end) {\n const { dayStart, dayOfWeek } = dayBucket(cursor)\n const dayEnd = dayStart + DAY_MS\n const sliceEnd = Math.min(range.end, dayEnd)\n\n const dayRules = own.filter(\n (r) => r.dayOfWeek === undefined || r.dayOfWeek === dayOfWeek,\n )\n if (dayRules.length === 0) return false\n\n // Build union of windows for this day, in absolute ms.\n const intervals = dayRules\n .map((r) => {\n const fromMs = parseHHMM(r.from, 0)\n const toMs = parseHHMM(r.to, DAY_MS)\n return { from: dayStart + fromMs, to: dayStart + toMs }\n })\n .filter((w) => w.to > w.from)\n .sort((a, b) => a.from - b.from)\n\n if (intervals.length === 0) return false\n\n const merged: Array<{ from: number; to: number }> = []\n for (const w of intervals) {\n const last = merged[merged.length - 1]\n if (last && w.from <= last.to) {\n last.to = Math.max(last.to, w.to)\n } else {\n merged.push({ ...w })\n }\n }\n\n // The required slice for this day is [cursor, sliceEnd). It must be\n // fully contained in the union. We sweep the merged windows.\n let needed = cursor\n for (const w of merged) {\n if (w.from > needed) return false\n if (w.to >= sliceEnd) {\n needed = sliceEnd\n break\n }\n needed = Math.max(needed, w.to)\n }\n if (needed < sliceEnd) return false\n\n cursor = sliceEnd\n }\n\n return true\n}\n","import { isAvailable } from './availability'\nimport type {\n Assignment,\n AvailabilityRule,\n CalendarScale,\n CalendarState,\n Conflict,\n Resource,\n} from './types'\n\nconst DAY_MS = 86_400_000\nconst STEP_MS: Record<CalendarScale, number> = {\n day: DAY_MS,\n week: 7 * DAY_MS,\n month: 30 * DAY_MS,\n}\n\nexport interface DetectConflictsArgs {\n scale?: CalendarScale\n rules?: readonly AvailabilityRule[]\n}\n\nfunction makeId(reason: Conflict['reason'], ids: readonly string[]): string {\n return `${reason}:${[...ids].sort().join(',')}`\n}\n\nfunction pushConflict(\n out: Map<string, Conflict>,\n reason: Conflict['reason'],\n ids: string[],\n): void {\n if (ids.length === 0) return\n const sorted = [...ids].sort()\n const id = makeId(reason, sorted)\n if (out.has(id)) return\n out.set(id, { id, reason, assignmentIds: sorted })\n}\n\n/**\n * Find all conflicts in `state`.\n *\n * Algorithm — three independent passes, results de-duplicated by\n * `(reason, sorted-ids)` so the same overlapping pair is never reported\n * twice as `overlap`:\n *\n * 1. **overlap** (per resource): sort assignments by `start` and sweep.\n * Keep an active set ordered by `end` ASC; whenever a new\n * assignment opens, every still-active assignment forms a pairwise\n * `overlap` conflict with it. O(n log n + k) where k = total\n * overlapping pairs.\n *\n * 2. **over-capacity** (per resource with `capacityPerSlot`): bucket\n * every assignment into the slots it touches (1 slot = 1 scale\n * step). For each slot whose concurrent count exceeds capacity,\n * emit one `over-capacity` conflict naming exactly the assignments\n * that touch that slot. This is the simple counter approach (no\n * interval tree) — fine up to ~10⁵ assignments per resource.\n *\n * 3. **outside-availability**: per-assignment check via\n * `isAvailable`. Each violation becomes its own one-element\n * conflict.\n */\nexport function detectConflicts(\n state: Pick<CalendarState, 'resources' | 'assignments'>,\n opts: DetectConflictsArgs = {},\n): Conflict[] {\n const scale = opts.scale ?? 'day'\n const step = STEP_MS[scale]\n const rules = opts.rules ?? []\n const conflicts = new Map<string, Conflict>()\n\n const byResource = new Map<string, Assignment[]>()\n for (const a of state.assignments) {\n let bucket = byResource.get(a.resourceId)\n if (!bucket) {\n bucket = []\n byResource.set(a.resourceId, bucket)\n }\n bucket.push(a)\n }\n\n // ── 1. Pairwise overlap (per resource) ─────────────────────────────\n for (const list of byResource.values()) {\n const sorted = [...list].sort((x, y) => x.start - y.start)\n for (let i = 0; i < sorted.length; i++) {\n const a = sorted[i]!\n for (let j = i + 1; j < sorted.length; j++) {\n const b = sorted[j]!\n if (b.start >= a.end) break\n // Touching boundaries (b.start === a.end) is NOT a conflict —\n // assignment ranges are half-open [start, end).\n pushConflict(conflicts, 'overlap', [a.id, b.id])\n }\n }\n }\n\n // ── 2. Over-capacity per slot (per resource with capacityPerSlot) ──\n const resourceById = new Map<string, Resource>()\n for (const r of state.resources) resourceById.set(r.id, r)\n\n for (const [rid, list] of byResource) {\n const resource = resourceById.get(rid)\n if (!resource || resource.capacityPerSlot === undefined) continue\n const cap = Math.max(0, Math.floor(resource.capacityPerSlot))\n\n const slotMap = new Map<number, string[]>()\n for (const a of list) {\n // Every slot the assignment touches.\n const lo = Math.floor(a.start / step) * step\n const hi = Math.ceil(a.end / step) * step\n for (let s = lo; s < hi; s += step) {\n let bucket = slotMap.get(s)\n if (!bucket) {\n bucket = []\n slotMap.set(s, bucket)\n }\n bucket.push(a.id)\n }\n }\n\n for (const ids of slotMap.values()) {\n if (ids.length > cap) {\n pushConflict(conflicts, 'over-capacity', ids)\n }\n }\n }\n\n // ── 3. Outside availability (per assignment) ───────────────────────\n if (rules.length > 0) {\n for (const a of state.assignments) {\n const r = resourceById.get(a.resourceId)\n if (!r) continue\n if (!isAvailable(r, { start: a.start, end: a.end }, rules)) {\n pushConflict(conflicts, 'outside-availability', [a.id])\n }\n }\n }\n\n return [...conflicts.values()]\n}\n","import type { Core } from '../../types'\nimport { CALENDAR_PREFIX } from '../namespaces'\nimport { detectConflicts } from './conflicts'\nimport type {\n Assignment,\n AvailabilityRule,\n CalendarEngine,\n CalendarEngineOptions,\n CalendarScale,\n CalendarState,\n Conflict,\n Resource,\n} from './types'\n\nlet calendarCounter = 0\n\n/**\n * Resource Calendar engine — the sixth member of the\n * form / table / tree / virtual / graph / **calendar** family.\n *\n * The in-memory maps are the source of truth and the public read API;\n * snapshots are mirrored to the Core store under\n * `$calendar.<id>.{resources,assignments,conflicts,view}` so other\n * engines / external observers can react via `core.subscribe(...)`.\n *\n * v0 scope:\n * - addResource / removeResource / updateResource\n * - addAssignment / updateAssignment / removeAssignment\n * - moveAssignment(id, deltaMs) — shifts both ends together\n * - resizeAssignment(id, side, deltaMs) — clamps so width >= 1ms\n * - setRules / setScale — re-compute conflicts on change\n * - detectConflicts under the hood; status of involved assignments\n * is auto-promoted to `'conflict'` so the React adapter can render\n * without re-running the algorithm.\n *\n * NOT in v0 (deferred to round-10):\n * - exception dates on `AvailabilityRule`\n * - full per-engine undo/redo (use `core.snapshot/restore` instead)\n * - aggregate read APIs over a sub-range\n */\nexport function createCalendar(\n core: Core,\n options?: CalendarEngineOptions,\n): CalendarEngine {\n const engineId = `c${calendarCounter++}${options?.name ? `-${options.name}` : ''}`\n const prefix = CALENDAR_PREFIX\n\n const resources = new Map<string, Resource>()\n const assignments = new Map<string, Assignment>()\n let rules: AvailabilityRule[] = options?.rules ? [...options.rules] : []\n let scale: CalendarScale = options?.scale ?? 'day'\n let conflicts: Conflict[] = []\n\n const localSubscribers = new Set<() => void>()\n\n function publish(): void {\n const resList = [...resources.values()]\n const assignList = [...assignments.values()]\n core.batch(() => {\n core.set(`${prefix}${engineId}.resources`, resList)\n core.set(`${prefix}${engineId}.assignments`, assignList)\n core.set(`${prefix}${engineId}.conflicts`, conflicts)\n core.set(`${prefix}${engineId}.view`, { scale })\n })\n for (const cb of localSubscribers) cb()\n }\n\n function recompute(): void {\n conflicts = detectConflicts(\n { resources: [...resources.values()], assignments: [...assignments.values()] },\n { scale, rules },\n )\n // Promote involved assignments to status: 'conflict'. Assignments\n // that aren't in any conflict get their original (or default)\n // status restored. We don't lose the underlying intent: the\n // promotion is reversible and recomputed on every mutation.\n const involved = new Set<string>()\n for (const c of conflicts) for (const id of c.assignmentIds) involved.add(id)\n for (const [id, a] of assignments) {\n const isConflicting = involved.has(id)\n if (isConflicting && a.status !== 'conflict') {\n assignments.set(id, { ...a, status: 'conflict' })\n } else if (!isConflicting && a.status === 'conflict') {\n // Drop the auto-`conflict` flag — the original intent (tentative\n // / confirmed / undefined) was lost when we promoted, and the\n // adapter only needs to know \"no longer conflicting\", so we\n // default back to `'tentative'` which is the safest fallback.\n assignments.set(id, { ...a, status: 'tentative' })\n }\n }\n }\n\n function commitAndPublish(): void {\n recompute()\n publish()\n }\n\n function subscribe(cb: () => void): () => void {\n localSubscribers.add(cb)\n return () => {\n localSubscribers.delete(cb)\n }\n }\n\n function addResource(resource: Resource): void {\n if (resources.has(resource.id)) {\n throw new Error(\n `[skygraph/calendar] resource id \"${resource.id}\" already exists`,\n )\n }\n resources.set(resource.id, { ...resource })\n commitAndPublish()\n }\n\n function removeResource(id: string): void {\n if (!resources.has(id)) return\n resources.delete(id)\n // Cascade: drop assignments tied to this resource.\n for (const [aid, a] of assignments) {\n if (a.resourceId === id) assignments.delete(aid)\n }\n commitAndPublish()\n }\n\n function updateResource(id: string, patch: Partial<Resource>): void {\n const r = resources.get(id)\n if (!r) throw new Error(`[skygraph/calendar] resource \"${id}\" not found`)\n resources.set(id, { ...r, ...patch, id })\n commitAndPublish()\n }\n\n function getResource(id: string): Resource | undefined {\n return resources.get(id)\n }\n\n function getResources(): Resource[] {\n return [...resources.values()]\n }\n\n function addAssignment(assignment: Assignment): void {\n if (assignments.has(assignment.id)) {\n throw new Error(\n `[skygraph/calendar] assignment id \"${assignment.id}\" already exists`,\n )\n }\n if (!resources.has(assignment.resourceId)) {\n throw new Error(\n `[skygraph/calendar] addAssignment: resource \"${assignment.resourceId}\" not found`,\n )\n }\n if (assignment.end <= assignment.start) {\n throw new Error(\n `[skygraph/calendar] addAssignment: end (${assignment.end}) must be greater than start (${assignment.start})`,\n )\n }\n assignments.set(assignment.id, { ...assignment })\n commitAndPublish()\n }\n\n function updateAssignment(id: string, patch: Partial<Assignment>): void {\n const a = assignments.get(id)\n if (!a) throw new Error(`[skygraph/calendar] assignment \"${id}\" not found`)\n const next: Assignment = { ...a, ...patch, id }\n if (next.end <= next.start) {\n throw new Error(\n `[skygraph/calendar] updateAssignment: end (${next.end}) must be greater than start (${next.start})`,\n )\n }\n if (!resources.has(next.resourceId)) {\n throw new Error(\n `[skygraph/calendar] updateAssignment: resource \"${next.resourceId}\" not found`,\n )\n }\n assignments.set(id, next)\n commitAndPublish()\n }\n\n function removeAssignment(id: string): void {\n if (!assignments.has(id)) return\n assignments.delete(id)\n commitAndPublish()\n }\n\n function moveAssignment(id: string, deltaMs: number): void {\n const a = assignments.get(id)\n if (!a) throw new Error(`[skygraph/calendar] assignment \"${id}\" not found`)\n if (deltaMs === 0) return\n assignments.set(id, { ...a, start: a.start + deltaMs, end: a.end + deltaMs })\n commitAndPublish()\n }\n\n function resizeAssignment(\n id: string,\n side: 'start' | 'end',\n deltaMs: number,\n ): void {\n const a = assignments.get(id)\n if (!a) throw new Error(`[skygraph/calendar] assignment \"${id}\" not found`)\n if (deltaMs === 0) return\n let next: Assignment\n if (side === 'end') {\n const nextEnd = Math.max(a.start + 1, a.end + deltaMs)\n next = { ...a, end: nextEnd }\n } else {\n const nextStart = Math.min(a.end - 1, a.start + deltaMs)\n next = { ...a, start: nextStart }\n }\n assignments.set(id, next)\n commitAndPublish()\n }\n\n function getAssignment(id: string): Assignment | undefined {\n return assignments.get(id)\n }\n\n function getAssignments(): Assignment[] {\n return [...assignments.values()]\n }\n\n function setRules(next: readonly AvailabilityRule[]): void {\n rules = [...next]\n commitAndPublish()\n }\n\n function getRules(): AvailabilityRule[] {\n return [...rules]\n }\n\n function getConflicts(): Conflict[] {\n return [...conflicts]\n }\n\n function setScale(next: CalendarScale): void {\n if (next === scale) return\n scale = next\n commitAndPublish()\n }\n\n function getScale(): CalendarScale {\n return scale\n }\n\n function getState(): CalendarState {\n return {\n resources: [...resources.values()],\n assignments: [...assignments.values()],\n conflicts: [...conflicts],\n }\n }\n\n function clear(): void {\n resources.clear()\n assignments.clear()\n rules = []\n conflicts = []\n publish()\n }\n\n publish() // initial empty publish so subscribers see a stable shape\n\n return {\n id: engineId,\n addResource,\n removeResource,\n updateResource,\n getResource,\n getResources,\n addAssignment,\n updateAssignment,\n removeAssignment,\n moveAssignment,\n resizeAssignment,\n getAssignment,\n getAssignments,\n setRules,\n getRules,\n getConflicts,\n setScale,\n getScale,\n getState,\n subscribe,\n clear,\n }\n}\n"],"mappings":"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","names":["DAY_MS"]}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// src/runtime/Node.ts
|
|
2
|
+
function createNode(id, value) {
|
|
3
|
+
return { id, value, version: 0 };
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/runtime/Store.ts
|
|
7
|
+
var Store = class {
|
|
8
|
+
nodes = /* @__PURE__ */ new Map();
|
|
9
|
+
has(path) {
|
|
10
|
+
return this.nodes.has(path);
|
|
11
|
+
}
|
|
12
|
+
get(path) {
|
|
13
|
+
return this.nodes.get(path)?.value;
|
|
14
|
+
}
|
|
15
|
+
getNode(path) {
|
|
16
|
+
return this.nodes.get(path);
|
|
17
|
+
}
|
|
18
|
+
set(path, value) {
|
|
19
|
+
let node = this.nodes.get(path);
|
|
20
|
+
if (node) {
|
|
21
|
+
node.value = value;
|
|
22
|
+
node.version++;
|
|
23
|
+
return node;
|
|
24
|
+
}
|
|
25
|
+
node = createNode(path, value);
|
|
26
|
+
this.nodes.set(path, node);
|
|
27
|
+
return node;
|
|
28
|
+
}
|
|
29
|
+
delete(path) {
|
|
30
|
+
return this.nodes.delete(path);
|
|
31
|
+
}
|
|
32
|
+
paths() {
|
|
33
|
+
return this.nodes.keys();
|
|
34
|
+
}
|
|
35
|
+
size() {
|
|
36
|
+
return this.nodes.size;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/runtime/SubscriptionEngine.ts
|
|
41
|
+
var SubscriptionEngine = class {
|
|
42
|
+
listeners = /* @__PURE__ */ new Map();
|
|
43
|
+
subscribe(path, cb) {
|
|
44
|
+
let set = this.listeners.get(path);
|
|
45
|
+
if (!set) {
|
|
46
|
+
set = /* @__PURE__ */ new Set();
|
|
47
|
+
this.listeners.set(path, set);
|
|
48
|
+
}
|
|
49
|
+
set.add(cb);
|
|
50
|
+
return () => {
|
|
51
|
+
set.delete(cb);
|
|
52
|
+
if (set.size === 0) {
|
|
53
|
+
this.listeners.delete(path);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
notify(path, value) {
|
|
58
|
+
const set = this.listeners.get(path);
|
|
59
|
+
if (!set) return;
|
|
60
|
+
for (const cb of set) {
|
|
61
|
+
cb(value);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
notifyMany(changes) {
|
|
65
|
+
for (const [path, value] of changes) {
|
|
66
|
+
this.notify(path, value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
hasListeners(path) {
|
|
70
|
+
const set = this.listeners.get(path);
|
|
71
|
+
return set !== void 0 && set.size > 0;
|
|
72
|
+
}
|
|
73
|
+
clear() {
|
|
74
|
+
this.listeners.clear();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/runtime/Batch.ts
|
|
79
|
+
var Batch = class {
|
|
80
|
+
depth = 0;
|
|
81
|
+
pending = /* @__PURE__ */ new Map();
|
|
82
|
+
get isActive() {
|
|
83
|
+
return this.depth > 0;
|
|
84
|
+
}
|
|
85
|
+
start() {
|
|
86
|
+
this.depth++;
|
|
87
|
+
}
|
|
88
|
+
end() {
|
|
89
|
+
this.depth--;
|
|
90
|
+
if (this.depth > 0) return null;
|
|
91
|
+
if (this.pending.size === 0) return null;
|
|
92
|
+
const changes = new Map(this.pending);
|
|
93
|
+
this.pending.clear();
|
|
94
|
+
return changes;
|
|
95
|
+
}
|
|
96
|
+
enqueue(path, value) {
|
|
97
|
+
this.pending.set(path, value);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/runtime/Transaction.ts
|
|
102
|
+
var Transaction = class {
|
|
103
|
+
active = false;
|
|
104
|
+
staged = [];
|
|
105
|
+
get isActive() {
|
|
106
|
+
return this.active;
|
|
107
|
+
}
|
|
108
|
+
start() {
|
|
109
|
+
if (this.active) {
|
|
110
|
+
throw new Error("SkyGraph: nested transactions are not supported");
|
|
111
|
+
}
|
|
112
|
+
this.active = true;
|
|
113
|
+
this.staged = [];
|
|
114
|
+
}
|
|
115
|
+
/** STAGE: record a write without mutating the store */
|
|
116
|
+
stage(path, value, prevValue) {
|
|
117
|
+
this.staged.push({ path, value, prevValue });
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* RESOLVE + COMMIT: drop unchanged values, apply the rest to store.
|
|
121
|
+
* Returns the map of actually changed paths and their new values.
|
|
122
|
+
*/
|
|
123
|
+
commit(store) {
|
|
124
|
+
const changes = /* @__PURE__ */ new Map();
|
|
125
|
+
for (const write of this.staged) {
|
|
126
|
+
if (Object.is(write.value, write.prevValue)) continue;
|
|
127
|
+
store.set(write.path, write.value);
|
|
128
|
+
changes.set(write.path, write.value);
|
|
129
|
+
}
|
|
130
|
+
this.active = false;
|
|
131
|
+
this.staged = [];
|
|
132
|
+
return changes;
|
|
133
|
+
}
|
|
134
|
+
getStaged() {
|
|
135
|
+
return this.staged;
|
|
136
|
+
}
|
|
137
|
+
complete() {
|
|
138
|
+
this.active = false;
|
|
139
|
+
this.staged = [];
|
|
140
|
+
}
|
|
141
|
+
rollback() {
|
|
142
|
+
this.active = false;
|
|
143
|
+
this.staged = [];
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// src/runtime/CycleDetector.ts
|
|
148
|
+
var CycleDetector = class {
|
|
149
|
+
graph = /* @__PURE__ */ new Map();
|
|
150
|
+
addEdge(from, to) {
|
|
151
|
+
let deps = this.graph.get(from);
|
|
152
|
+
if (!deps) {
|
|
153
|
+
deps = /* @__PURE__ */ new Set();
|
|
154
|
+
this.graph.set(from, deps);
|
|
155
|
+
}
|
|
156
|
+
deps.add(to);
|
|
157
|
+
if (this.hasCycle(to, /* @__PURE__ */ new Set([from]))) {
|
|
158
|
+
deps.delete(to);
|
|
159
|
+
if (deps.size === 0) this.graph.delete(from);
|
|
160
|
+
throw new Error(
|
|
161
|
+
`SkyGraph: circular dependency detected: ${from} -> ${to}`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
removeNode(id) {
|
|
166
|
+
this.graph.delete(id);
|
|
167
|
+
for (const deps of this.graph.values()) {
|
|
168
|
+
deps.delete(id);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
getDependents(id) {
|
|
172
|
+
const targets = this.graph.get(id);
|
|
173
|
+
return targets ? [...targets] : [];
|
|
174
|
+
}
|
|
175
|
+
hasCycle(node, visited) {
|
|
176
|
+
if (visited.has(node)) return true;
|
|
177
|
+
const deps = this.graph.get(node);
|
|
178
|
+
if (!deps) return false;
|
|
179
|
+
visited.add(node);
|
|
180
|
+
for (const dep of deps) {
|
|
181
|
+
if (this.hasCycle(dep, new Set(visited))) return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// src/runtime/ComputedEngine.ts
|
|
188
|
+
var ComputedEngine = class {
|
|
189
|
+
computedNodes = /* @__PURE__ */ new Map();
|
|
190
|
+
cycleDetector = new CycleDetector();
|
|
191
|
+
register(target, deps, fn) {
|
|
192
|
+
if (this.computedNodes.has(target)) {
|
|
193
|
+
this.remove(target);
|
|
194
|
+
}
|
|
195
|
+
for (const dep of deps) {
|
|
196
|
+
this.cycleDetector.addEdge(dep, target);
|
|
197
|
+
}
|
|
198
|
+
this.computedNodes.set(target, { target, deps, fn });
|
|
199
|
+
}
|
|
200
|
+
remove(target) {
|
|
201
|
+
this.computedNodes.delete(target);
|
|
202
|
+
this.cycleDetector.removeNode(target);
|
|
203
|
+
}
|
|
204
|
+
isComputed(path) {
|
|
205
|
+
return this.computedNodes.has(path);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Like propagate, but returns both new and old values for middleware support.
|
|
209
|
+
* Does NOT write to the store — the caller handles that via dispatchWrite.
|
|
210
|
+
*/
|
|
211
|
+
propagateRaw(changedPaths, store) {
|
|
212
|
+
const result = /* @__PURE__ */ new Map();
|
|
213
|
+
const visited = /* @__PURE__ */ new Set();
|
|
214
|
+
const queue = [...changedPaths];
|
|
215
|
+
while (queue.length > 0) {
|
|
216
|
+
const path = queue.shift();
|
|
217
|
+
if (visited.has(path)) continue;
|
|
218
|
+
visited.add(path);
|
|
219
|
+
const dependents = this.cycleDetector.getDependents(path);
|
|
220
|
+
for (const depTarget of dependents) {
|
|
221
|
+
const computed = this.computedNodes.get(depTarget);
|
|
222
|
+
if (!computed) continue;
|
|
223
|
+
const depValues = computed.deps.map((d) => {
|
|
224
|
+
const pending = result.get(d);
|
|
225
|
+
return pending ? pending.value : store.get(d);
|
|
226
|
+
});
|
|
227
|
+
const newValue = computed.fn(...depValues);
|
|
228
|
+
const oldValue = result.has(depTarget) ? result.get(depTarget).oldValue : store.get(depTarget);
|
|
229
|
+
if (!Object.is(newValue, oldValue)) {
|
|
230
|
+
result.set(depTarget, { value: newValue, oldValue });
|
|
231
|
+
queue.push(depTarget);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Initialize all computed values based on current store state.
|
|
239
|
+
*/
|
|
240
|
+
initializeAll(store) {
|
|
241
|
+
for (const computed of this.computedNodes.values()) {
|
|
242
|
+
const depValues = computed.deps.map((d) => store.get(d));
|
|
243
|
+
const value = computed.fn(...depValues);
|
|
244
|
+
store.set(computed.target, value);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export {
|
|
250
|
+
createNode,
|
|
251
|
+
Store,
|
|
252
|
+
SubscriptionEngine,
|
|
253
|
+
Batch,
|
|
254
|
+
Transaction,
|
|
255
|
+
CycleDetector,
|
|
256
|
+
ComputedEngine
|
|
257
|
+
};
|
|
258
|
+
//# sourceMappingURL=chunk-GEMALROQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../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"],"sourcesContent":["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"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// src/engines/virtual/measure.ts
|
|
2
|
+
function createMeasureCache(options) {
|
|
3
|
+
let itemCount = options.itemCount;
|
|
4
|
+
let estimate = options.estimate;
|
|
5
|
+
const measured = /* @__PURE__ */ new Map();
|
|
6
|
+
let offsets = null;
|
|
7
|
+
let cachedTotal = 0;
|
|
8
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
9
|
+
const getEstimate = (index) => typeof estimate === "number" ? estimate : estimate(index);
|
|
10
|
+
const getItemSize = (index) => {
|
|
11
|
+
const m = measured.get(index);
|
|
12
|
+
return m !== void 0 ? m : getEstimate(index);
|
|
13
|
+
};
|
|
14
|
+
const rebuildOffsets = () => {
|
|
15
|
+
if (offsets && offsets.length === itemCount) return;
|
|
16
|
+
const arr = new Array(itemCount);
|
|
17
|
+
let acc = 0;
|
|
18
|
+
for (let i = 0; i < itemCount; i++) {
|
|
19
|
+
arr[i] = acc;
|
|
20
|
+
acc += getItemSize(i);
|
|
21
|
+
}
|
|
22
|
+
offsets = arr;
|
|
23
|
+
cachedTotal = acc;
|
|
24
|
+
};
|
|
25
|
+
const getTotalHeight = () => {
|
|
26
|
+
rebuildOffsets();
|
|
27
|
+
return cachedTotal;
|
|
28
|
+
};
|
|
29
|
+
const getItemOffset = (index) => {
|
|
30
|
+
if (index <= 0) return 0;
|
|
31
|
+
if (index >= itemCount) return getTotalHeight();
|
|
32
|
+
rebuildOffsets();
|
|
33
|
+
return offsets[index];
|
|
34
|
+
};
|
|
35
|
+
const getItemAtOffset = (offset) => {
|
|
36
|
+
if (itemCount === 0) return 0;
|
|
37
|
+
rebuildOffsets();
|
|
38
|
+
let lo = 0;
|
|
39
|
+
let hi = itemCount - 1;
|
|
40
|
+
while (lo < hi) {
|
|
41
|
+
const mid = lo + hi + 1 >>> 1;
|
|
42
|
+
if (offsets[mid] <= offset) lo = mid;
|
|
43
|
+
else hi = mid - 1;
|
|
44
|
+
}
|
|
45
|
+
return lo;
|
|
46
|
+
};
|
|
47
|
+
const invalidate = (event) => {
|
|
48
|
+
offsets = null;
|
|
49
|
+
for (const l of listeners) l(event);
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
get itemCount() {
|
|
53
|
+
return itemCount;
|
|
54
|
+
},
|
|
55
|
+
get measuredCount() {
|
|
56
|
+
return measured.size;
|
|
57
|
+
},
|
|
58
|
+
setItemCount(count) {
|
|
59
|
+
if (count === itemCount) return;
|
|
60
|
+
if (count < itemCount && measured.size > 0) {
|
|
61
|
+
for (const k of [...measured.keys()]) {
|
|
62
|
+
if (k >= count) measured.delete(k);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
itemCount = count;
|
|
66
|
+
invalidate("resize");
|
|
67
|
+
},
|
|
68
|
+
setEstimate(e) {
|
|
69
|
+
estimate = e;
|
|
70
|
+
invalidate("reset");
|
|
71
|
+
},
|
|
72
|
+
setMeasuredHeight(index, height) {
|
|
73
|
+
if (index < 0 || index >= itemCount) return false;
|
|
74
|
+
if (!Number.isFinite(height) || height < 0) return false;
|
|
75
|
+
const prev = measured.get(index);
|
|
76
|
+
if (prev === height) return false;
|
|
77
|
+
measured.set(index, height);
|
|
78
|
+
invalidate("measure");
|
|
79
|
+
return true;
|
|
80
|
+
},
|
|
81
|
+
clearMeasured(index) {
|
|
82
|
+
const had = measured.delete(index);
|
|
83
|
+
if (had) invalidate("measure");
|
|
84
|
+
return had;
|
|
85
|
+
},
|
|
86
|
+
reset() {
|
|
87
|
+
if (measured.size === 0) return;
|
|
88
|
+
measured.clear();
|
|
89
|
+
invalidate("reset");
|
|
90
|
+
},
|
|
91
|
+
hasMeasured(index) {
|
|
92
|
+
return measured.has(index);
|
|
93
|
+
},
|
|
94
|
+
getMeasuredHeight(index) {
|
|
95
|
+
return measured.get(index);
|
|
96
|
+
},
|
|
97
|
+
getEstimatedSize: getEstimate,
|
|
98
|
+
getItemSize,
|
|
99
|
+
getItemOffset,
|
|
100
|
+
getItemAtOffset,
|
|
101
|
+
getTotalHeight,
|
|
102
|
+
subscribe(listener) {
|
|
103
|
+
listeners.add(listener);
|
|
104
|
+
return () => {
|
|
105
|
+
listeners.delete(listener);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/engines/virtual/index.ts
|
|
112
|
+
function createVirtual(options) {
|
|
113
|
+
let itemCount = options.itemCount;
|
|
114
|
+
let itemHeight = options.itemHeight;
|
|
115
|
+
const overscan = options.overscan ?? 5;
|
|
116
|
+
let cache = null;
|
|
117
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
118
|
+
const ensureCache = () => {
|
|
119
|
+
if (cache) return cache;
|
|
120
|
+
cache = createMeasureCache({ itemCount, estimate: itemHeight });
|
|
121
|
+
cache.subscribe((event) => {
|
|
122
|
+
for (const l of listeners) l(event);
|
|
123
|
+
});
|
|
124
|
+
return cache;
|
|
125
|
+
};
|
|
126
|
+
const isFastPath = () => typeof itemHeight === "number" && (cache === null || cache.measuredCount === 0);
|
|
127
|
+
const getEstimate = (index) => typeof itemHeight === "number" ? itemHeight : itemHeight(index);
|
|
128
|
+
const getItemSize = (index) => {
|
|
129
|
+
if (cache) return cache.getItemSize(index);
|
|
130
|
+
return getEstimate(index);
|
|
131
|
+
};
|
|
132
|
+
const getTotalHeight = () => {
|
|
133
|
+
if (isFastPath()) return itemCount * itemHeight;
|
|
134
|
+
return ensureCache().getTotalHeight();
|
|
135
|
+
};
|
|
136
|
+
const getItemOffset = (index) => {
|
|
137
|
+
if (index <= 0) return 0;
|
|
138
|
+
if (index >= itemCount) return getTotalHeight();
|
|
139
|
+
if (isFastPath()) return index * itemHeight;
|
|
140
|
+
return ensureCache().getItemOffset(index);
|
|
141
|
+
};
|
|
142
|
+
const getItemAtOffset = (offset) => {
|
|
143
|
+
if (itemCount === 0) return 0;
|
|
144
|
+
if (isFastPath()) {
|
|
145
|
+
const h = itemHeight;
|
|
146
|
+
return Math.min(Math.floor(offset / h), itemCount - 1);
|
|
147
|
+
}
|
|
148
|
+
return ensureCache().getItemAtOffset(offset);
|
|
149
|
+
};
|
|
150
|
+
const getRange = (scrollTop, viewportHeight) => {
|
|
151
|
+
if (itemCount === 0) {
|
|
152
|
+
return { startIndex: 0, endIndex: 0, offsetTop: 0, totalHeight: 0, visibleItems: [] };
|
|
153
|
+
}
|
|
154
|
+
const total = getTotalHeight();
|
|
155
|
+
const rawStart = getItemAtOffset(scrollTop);
|
|
156
|
+
const startIndex = Math.max(0, rawStart - overscan);
|
|
157
|
+
let endIndex = rawStart;
|
|
158
|
+
let accHeight = getItemOffset(rawStart) + getItemSize(rawStart);
|
|
159
|
+
const bottomEdge = scrollTop + viewportHeight;
|
|
160
|
+
while (endIndex < itemCount - 1 && accHeight < bottomEdge) {
|
|
161
|
+
endIndex++;
|
|
162
|
+
accHeight += getItemSize(endIndex);
|
|
163
|
+
}
|
|
164
|
+
endIndex = Math.min(itemCount - 1, endIndex + overscan);
|
|
165
|
+
const visibleItems = [];
|
|
166
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
167
|
+
visibleItems.push({
|
|
168
|
+
index: i,
|
|
169
|
+
offsetTop: getItemOffset(i),
|
|
170
|
+
height: getItemSize(i)
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
startIndex,
|
|
175
|
+
endIndex,
|
|
176
|
+
offsetTop: getItemOffset(startIndex),
|
|
177
|
+
totalHeight: total,
|
|
178
|
+
visibleItems
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
const scrollToIndex = (index, viewportHeight, align = "start") => {
|
|
182
|
+
const clamped = Math.max(0, Math.min(index, itemCount - 1));
|
|
183
|
+
const offset = getItemOffset(clamped);
|
|
184
|
+
const h = getItemSize(clamped);
|
|
185
|
+
switch (align) {
|
|
186
|
+
case "center":
|
|
187
|
+
return Math.max(0, offset - viewportHeight / 2 + h / 2);
|
|
188
|
+
case "end":
|
|
189
|
+
return Math.max(0, offset - viewportHeight + h);
|
|
190
|
+
default:
|
|
191
|
+
return offset;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const emit = (event) => {
|
|
195
|
+
for (const l of listeners) l(event);
|
|
196
|
+
};
|
|
197
|
+
return {
|
|
198
|
+
get itemCount() {
|
|
199
|
+
return itemCount;
|
|
200
|
+
},
|
|
201
|
+
get totalHeight() {
|
|
202
|
+
return getTotalHeight();
|
|
203
|
+
},
|
|
204
|
+
get measuredCount() {
|
|
205
|
+
return cache?.measuredCount ?? 0;
|
|
206
|
+
},
|
|
207
|
+
setItemCount(count) {
|
|
208
|
+
if (count === itemCount) return;
|
|
209
|
+
itemCount = count;
|
|
210
|
+
if (cache) {
|
|
211
|
+
cache.setItemCount(count);
|
|
212
|
+
} else {
|
|
213
|
+
emit("resize");
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
setItemHeight(height) {
|
|
217
|
+
itemHeight = height;
|
|
218
|
+
if (cache) {
|
|
219
|
+
cache.setEstimate(height);
|
|
220
|
+
} else {
|
|
221
|
+
emit("reset");
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
setMeasuredHeight(index, height) {
|
|
225
|
+
return ensureCache().setMeasuredHeight(index, height);
|
|
226
|
+
},
|
|
227
|
+
clearMeasuredHeight(index) {
|
|
228
|
+
if (!cache) return false;
|
|
229
|
+
return cache.clearMeasured(index);
|
|
230
|
+
},
|
|
231
|
+
resetMeasurements() {
|
|
232
|
+
if (cache) cache.reset();
|
|
233
|
+
},
|
|
234
|
+
hasMeasured(index) {
|
|
235
|
+
return cache ? cache.hasMeasured(index) : false;
|
|
236
|
+
},
|
|
237
|
+
getEstimatedSize: getEstimate,
|
|
238
|
+
getItemSize,
|
|
239
|
+
getRange,
|
|
240
|
+
getItemOffset,
|
|
241
|
+
getItemAtOffset,
|
|
242
|
+
scrollToIndex,
|
|
243
|
+
subscribe(listener) {
|
|
244
|
+
listeners.add(listener);
|
|
245
|
+
return () => {
|
|
246
|
+
listeners.delete(listener);
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export {
|
|
253
|
+
createMeasureCache,
|
|
254
|
+
createVirtual
|
|
255
|
+
};
|
|
256
|
+
//# sourceMappingURL=chunk-IY6LJU3L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/engines/virtual/measure.ts","../src/engines/virtual/index.ts"],"sourcesContent":["/**\n * Кэш измерений высоты для VirtualEngine с динамической высотой строк.\n *\n * Хранит реальные измеренные высоты `Map<index, number>` поверх эстимейта.\n * Префикс-сумма (offsets) пересобирается лениво при первом запросе после\n * любой инвалидации (resize / measure / reset). Подписки нужны React-адаптеру,\n * чтобы перерисоваться, когда измерение строки изменилось.\n */\n\nexport type MeasureCacheEvent = 'measure' | 'reset' | 'resize'\nexport type MeasureCacheListener = (event: MeasureCacheEvent) => void\n\nexport interface MeasureCacheOptions {\n itemCount: number\n estimate: number | ((index: number) => number)\n}\n\nexport interface MeasureCache {\n readonly itemCount: number\n /** Сколько строк уже было измерено. */\n readonly measuredCount: number\n\n setItemCount(count: number): void\n setEstimate(estimate: number | ((index: number) => number)): void\n\n /** Записать измеренную высоту. Возвращает `true`, если значение изменилось. */\n setMeasuredHeight(index: number, height: number): boolean\n /** Сбросить измерение для одной строки. */\n clearMeasured(index: number): boolean\n /** Сбросить все измерения. */\n reset(): void\n\n hasMeasured(index: number): boolean\n getMeasuredHeight(index: number): number | undefined\n\n /** Эстимейт высоты — без учёта измерений. */\n getEstimatedSize(index: number): number\n /** Эффективная высота строки (измерение или эстимейт). */\n getItemSize(index: number): number\n\n getItemOffset(index: number): number\n getItemAtOffset(offset: number): number\n getTotalHeight(): number\n\n subscribe(listener: MeasureCacheListener): () => void\n}\n\nexport function createMeasureCache(options: MeasureCacheOptions): MeasureCache {\n let itemCount = options.itemCount\n let estimate = options.estimate\n const measured = new Map<number, number>()\n let offsets: number[] | null = null\n let cachedTotal = 0\n const listeners = new Set<MeasureCacheListener>()\n\n const getEstimate = (index: number): number =>\n typeof estimate === 'number' ? estimate : estimate(index)\n\n const getItemSize = (index: number): number => {\n const m = measured.get(index)\n return m !== undefined ? m : getEstimate(index)\n }\n\n const rebuildOffsets = () => {\n if (offsets && offsets.length === itemCount) return\n const arr = new Array<number>(itemCount)\n let acc = 0\n for (let i = 0; i < itemCount; i++) {\n arr[i] = acc\n acc += getItemSize(i)\n }\n offsets = arr\n cachedTotal = acc\n }\n\n const getTotalHeight = (): number => {\n rebuildOffsets()\n return cachedTotal\n }\n\n const getItemOffset = (index: number): number => {\n if (index <= 0) return 0\n if (index >= itemCount) return getTotalHeight()\n rebuildOffsets()\n return offsets![index]!\n }\n\n const getItemAtOffset = (offset: number): number => {\n if (itemCount === 0) return 0\n rebuildOffsets()\n let lo = 0\n let hi = itemCount - 1\n while (lo < hi) {\n const mid = (lo + hi + 1) >>> 1\n if (offsets![mid]! <= offset) lo = mid\n else hi = mid - 1\n }\n return lo\n }\n\n const invalidate = (event: MeasureCacheEvent) => {\n offsets = null\n for (const l of listeners) l(event)\n }\n\n return {\n get itemCount() {\n return itemCount\n },\n get measuredCount() {\n return measured.size\n },\n\n setItemCount(count: number) {\n if (count === itemCount) return\n if (count < itemCount && measured.size > 0) {\n for (const k of [...measured.keys()]) {\n if (k >= count) measured.delete(k)\n }\n }\n itemCount = count\n invalidate('resize')\n },\n\n setEstimate(e: number | ((index: number) => number)) {\n estimate = e\n invalidate('reset')\n },\n\n setMeasuredHeight(index: number, height: number): boolean {\n if (index < 0 || index >= itemCount) return false\n if (!Number.isFinite(height) || height < 0) return false\n const prev = measured.get(index)\n if (prev === height) return false\n measured.set(index, height)\n invalidate('measure')\n return true\n },\n\n clearMeasured(index: number): boolean {\n const had = measured.delete(index)\n if (had) invalidate('measure')\n return had\n },\n\n reset() {\n if (measured.size === 0) return\n measured.clear()\n invalidate('reset')\n },\n\n hasMeasured(index: number) {\n return measured.has(index)\n },\n getMeasuredHeight(index: number) {\n return measured.get(index)\n },\n\n getEstimatedSize: getEstimate,\n getItemSize,\n getItemOffset,\n getItemAtOffset,\n getTotalHeight,\n\n subscribe(listener: MeasureCacheListener) {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n }\n}\n","import { createMeasureCache, type MeasureCache } from './measure'\n\nexport type {\n MeasureCache,\n MeasureCacheEvent,\n MeasureCacheListener,\n MeasureCacheOptions,\n} from './measure'\nexport { createMeasureCache } from './measure'\n\nexport interface VirtualRange {\n startIndex: number\n endIndex: number\n offsetTop: number\n totalHeight: number\n visibleItems: VirtualItem[]\n}\n\nexport interface VirtualItem {\n index: number\n offsetTop: number\n height: number\n}\n\n/** Событие инвалидации, эмитимое VirtualEngine подписчикам. */\nexport type VirtualEngineEvent = 'measure' | 'reset' | 'resize'\nexport type VirtualEngineListener = (event: VirtualEngineEvent) => void\n\nexport interface VirtualEngineOptions {\n itemCount: number\n itemHeight: number | ((index: number) => number)\n overscan?: number\n}\n\nexport interface VirtualEngine {\n readonly itemCount: number\n readonly totalHeight: number\n /** Сколько строк прошли через `setMeasuredHeight`. */\n readonly measuredCount: number\n\n setItemCount(count: number): void\n setItemHeight(height: number | ((index: number) => number)): void\n\n /**\n * Записать реально измеренную (через ResizeObserver) высоту строки.\n * Возвращает `true`, если значение изменилось — полезно для контроля\n * количества `setState` в адаптере.\n */\n setMeasuredHeight(index: number, height: number): boolean\n /** Сбросить измерение для одной строки (высота вернётся к эстимейту). */\n clearMeasuredHeight(index: number): boolean\n /** Сбросить все измерения (например, при resize контейнера / смене темы). */\n resetMeasurements(): void\n /** Был ли индекс уже измерён. */\n hasMeasured(index: number): boolean\n /** Эстимейт высоты строки без учёта измерений. */\n getEstimatedSize(index: number): number\n /** Эффективная высота строки — измерение, если есть, иначе эстимейт. */\n getItemSize(index: number): number\n\n getRange(scrollTop: number, viewportHeight: number): VirtualRange\n getItemOffset(index: number): number\n getItemAtOffset(offset: number): number\n scrollToIndex(\n index: number,\n viewportHeight: number,\n align?: 'start' | 'center' | 'end',\n ): number\n\n /**\n * Подписаться на инвалидации (resize / measure / reset). Вызывается\n * только после реального изменения — повторные `setMeasuredHeight`\n * с тем же значением событие не эмитят.\n */\n subscribe(listener: VirtualEngineListener): () => void\n}\n\nexport function createVirtual(options: VirtualEngineOptions): VirtualEngine {\n let itemCount = options.itemCount\n let itemHeight = options.itemHeight\n const overscan = options.overscan ?? 5\n\n // Кэш создаётся лениво — для чисто фиксированной высоты без измерений\n // нам он не нужен, и мы сохраняем O(1) арифметику для миллионов строк.\n let cache: MeasureCache | null = null\n const listeners = new Set<VirtualEngineListener>()\n\n const ensureCache = (): MeasureCache => {\n if (cache) return cache\n cache = createMeasureCache({ itemCount, estimate: itemHeight })\n cache.subscribe((event) => {\n for (const l of listeners) l(event)\n })\n return cache\n }\n\n const isFastPath = (): boolean =>\n typeof itemHeight === 'number' && (cache === null || cache.measuredCount === 0)\n\n const getEstimate = (index: number): number =>\n typeof itemHeight === 'number' ? itemHeight : itemHeight(index)\n\n const getItemSize = (index: number): number => {\n if (cache) return cache.getItemSize(index)\n return getEstimate(index)\n }\n\n const getTotalHeight = (): number => {\n if (isFastPath()) return itemCount * (itemHeight as number)\n return ensureCache().getTotalHeight()\n }\n\n const getItemOffset = (index: number): number => {\n if (index <= 0) return 0\n if (index >= itemCount) return getTotalHeight()\n if (isFastPath()) return index * (itemHeight as number)\n return ensureCache().getItemOffset(index)\n }\n\n const getItemAtOffset = (offset: number): number => {\n if (itemCount === 0) return 0\n if (isFastPath()) {\n const h = itemHeight as number\n return Math.min(Math.floor(offset / h), itemCount - 1)\n }\n return ensureCache().getItemAtOffset(offset)\n }\n\n const getRange = (scrollTop: number, viewportHeight: number): VirtualRange => {\n if (itemCount === 0) {\n return { startIndex: 0, endIndex: 0, offsetTop: 0, totalHeight: 0, visibleItems: [] }\n }\n\n const total = getTotalHeight()\n const rawStart = getItemAtOffset(scrollTop)\n const startIndex = Math.max(0, rawStart - overscan)\n\n let endIndex = rawStart\n let accHeight = getItemOffset(rawStart) + getItemSize(rawStart)\n const bottomEdge = scrollTop + viewportHeight\n\n while (endIndex < itemCount - 1 && accHeight < bottomEdge) {\n endIndex++\n accHeight += getItemSize(endIndex)\n }\n endIndex = Math.min(itemCount - 1, endIndex + overscan)\n\n const visibleItems: VirtualItem[] = []\n for (let i = startIndex; i <= endIndex; i++) {\n visibleItems.push({\n index: i,\n offsetTop: getItemOffset(i),\n height: getItemSize(i),\n })\n }\n\n return {\n startIndex,\n endIndex,\n offsetTop: getItemOffset(startIndex),\n totalHeight: total,\n visibleItems,\n }\n }\n\n const scrollToIndex = (\n index: number,\n viewportHeight: number,\n align: 'start' | 'center' | 'end' = 'start',\n ): number => {\n const clamped = Math.max(0, Math.min(index, itemCount - 1))\n const offset = getItemOffset(clamped)\n const h = getItemSize(clamped)\n\n switch (align) {\n case 'center':\n return Math.max(0, offset - viewportHeight / 2 + h / 2)\n case 'end':\n return Math.max(0, offset - viewportHeight + h)\n default:\n return offset\n }\n }\n\n const emit = (event: VirtualEngineEvent) => {\n for (const l of listeners) l(event)\n }\n\n return {\n get itemCount() {\n return itemCount\n },\n get totalHeight() {\n return getTotalHeight()\n },\n get measuredCount() {\n return cache?.measuredCount ?? 0\n },\n\n setItemCount(count: number) {\n if (count === itemCount) return\n itemCount = count\n if (cache) {\n cache.setItemCount(count)\n } else {\n emit('resize')\n }\n },\n\n setItemHeight(height: number | ((index: number) => number)) {\n itemHeight = height\n if (cache) {\n cache.setEstimate(height)\n } else {\n emit('reset')\n }\n },\n\n setMeasuredHeight(index: number, height: number) {\n return ensureCache().setMeasuredHeight(index, height)\n },\n\n clearMeasuredHeight(index: number) {\n if (!cache) return false\n return cache.clearMeasured(index)\n },\n\n resetMeasurements() {\n if (cache) cache.reset()\n },\n\n hasMeasured(index: number) {\n return cache ? cache.hasMeasured(index) : false\n },\n\n getEstimatedSize: getEstimate,\n getItemSize,\n\n getRange,\n getItemOffset,\n getItemAtOffset,\n scrollToIndex,\n\n subscribe(listener: VirtualEngineListener) {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n }\n}\n"],"mappings":"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","names":[]}
|