chore(quark): sync reactive kernel and rename package to @opencode-ai/quark

This commit is contained in:
Kit Langton
2026-07-17 23:45:56 -04:00
parent bf10f9d47d
commit 2a8ad383ea
9 changed files with 119 additions and 135 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "effect-quark",
"name": "@opencode-ai/quark",
"version": "0.0.0",
"private": true,
"type": "module",
+108 -124
View File
@@ -74,13 +74,17 @@ export namespace Layout {
make(initial?: readonly A[], options?: { readonly metrics?: Keyed.Metrics }): Keyed.Keyed<A, Key>
}
type IndexField<A> = A extends unknown ? keyof A : never
export interface MembersIndex<A, Member> {
readonly type: "members"
readonly fields?: readonly IndexField<A>[]
readonly extract: (value: A) => Iterable<Member>
}
export interface FirstIndex<A> {
readonly type: "first"
readonly fields?: readonly IndexField<A>[]
readonly matches: (value: A) => boolean
}
@@ -114,7 +118,7 @@ export namespace Layout {
/**
* Comparator backend. "generated" (the default) compiles one specialized
* Function per plan; "closure" is the compatibility fallback for
* Function per plan operation; "closure" is the compatibility fallback for
* environments that forbid runtime code generation (CSP).
*/
export interface CompileOptions {
@@ -123,7 +127,9 @@ export namespace Layout {
export interface IndexBuilder<A> {
members<Member>(extract: (value: A) => Iterable<Member>): MembersIndex<A, Member>
members<Member>(fields: readonly IndexField<A>[], extract: (value: A) => Iterable<Member>): MembersIndex<A, Member>
first(matches: (value: A) => boolean): FirstIndex<A>
first(fields: readonly IndexField<A>[], matches: (value: A) => boolean): FirstIndex<A>
}
export interface Collection<A, Key, Definitions extends Indexes<A>> extends Keyed.Keyed<A, Key> {
@@ -270,8 +276,14 @@ export namespace Layout {
export function collection(input: unknown, define: unknown, options?: CompileOptions): unknown {
const plan = compile(input as never, options) as Plan<unknown, unknown>
const definitions = (define as (index: IndexBuilder<unknown>) => Indexes<unknown>)({
members: (extract) => ({ type: "members", extract }),
first: (matches) => ({ type: "first", matches }),
members: ((fieldsOrExtract: readonly PropertyKey[] | ((value: unknown) => Iterable<unknown>), extract?: (value: unknown) => Iterable<unknown>) =>
typeof fieldsOrExtract === "function"
? { type: "members", extract: fieldsOrExtract }
: { type: "members", fields: fieldsOrExtract, extract: extract! }) as IndexBuilder<unknown>["members"],
first: ((fieldsOrMatches: readonly PropertyKey[] | ((value: unknown) => boolean), matches?: (value: unknown) => boolean) =>
typeof fieldsOrMatches === "function"
? { type: "first", matches: fieldsOrMatches }
: { type: "first", fields: fieldsOrMatches, matches: matches! }) as IndexBuilder<unknown>["first"],
})
return {
...plan,
@@ -314,52 +326,22 @@ export namespace Layout {
): Collection<A, Key, Definitions> {
// `pending` hands a diff the collection already computed for a staged
// mutation to the comparator, so equivalence costs one comparison, not two.
let pending: number | ReadonlyMap<Key, number> | undefined
let pending: { readonly previous: A; readonly next: A; readonly mask: number } | undefined
const values = Keyed.make<A, Key>({
key: plan.keyOf,
equivalent(left, right) {
const mask = typeof pending === "number" ? pending : pending?.get(plan.keyOf(left))
return (mask ?? plan.diff(left, right)) === 0
if (pending && pending.previous === left && pending.next === right) {
const mask = pending.mask
pending = undefined
return mask === 0
}
return plan.diff(left, right) === 0
},
metrics: options?.metrics,
})
// Projection dependency tracker: extract/matches run against a proxy that
// records which top-level fields they read, as a bitmask aligned with
// plan.diff. Enumerating keys or reading an unknown property is
// conservative (all bits). Soundness: a pure projection re-reads a field
// only if the fields it previously read are unchanged or it is re-run.
let trackTarget: Record<PropertyKey, unknown> | undefined
let trackMask = 0
const trackProxy = new Proxy({} as Record<PropertyKey, unknown>, {
get(_, property) {
trackMask |= plan.bits.get(property) ?? -1
return trackTarget![property]
},
has(_, property) {
trackMask |= plan.bits.get(property) ?? -1
return property in trackTarget!
},
ownKeys() {
trackMask = -1
return Reflect.ownKeys(trackTarget!)
},
getOwnPropertyDescriptor(_, property) {
trackMask = -1
const descriptor = Reflect.getOwnPropertyDescriptor(trackTarget!, property)
return descriptor && { ...descriptor, configurable: true }
},
})
function tracked<T>(value: A, f: (value: A) => T) {
trackTarget = value as Record<PropertyKey, unknown>
trackMask = 0
try {
const result = f(trackProxy as A)
return { result, reads: trackMask }
} finally {
trackTarget = undefined
trackMask = 0
}
function dependencyMask(fields: readonly PropertyKey[] | undefined) {
return fields?.reduce<number>((mask, field) => mask | (plan.bits.get(normalizeName(field)) ?? -1), 0) ?? -1
}
// Each index entry stages user-code projections before any state mutation
@@ -369,20 +351,23 @@ export namespace Layout {
type MemberChange = { readonly add?: readonly unknown[]; readonly remove?: readonly unknown[] }
interface Entry {
readonly name: PropertyKey
reads(key: Key): number | undefined
readonly reads: number
stage(key: Key, value: A, mode: "add" | "replace"): Commit
applyDelta?(key: Key, change: MemberChange): void
remove(key: Key, slot?: Readable<A>): void
afterPlacement(slot: Readable<A>): void
afterRebuild(): void
afterPlacement?(slot: Readable<A>): void
afterRebuild?(): void
member?(candidate: unknown): boolean
firstSlot?(): Readable<A> | undefined
}
function membersEntry(name: PropertyKey, extract: (value: A) => Iterable<unknown>): Entry {
function membersEntry(
name: PropertyKey,
fields: readonly PropertyKey[] | undefined,
extract: (value: A) => Iterable<unknown>,
): Entry {
const counts = new Map<unknown, number>()
const byKey = new Map<Key, { readonly source: Iterable<unknown>; readonly members: Set<unknown> }>()
const readsByKey = new Map<Key, number>()
function adjust(member: unknown, amount: 1 | -1) {
const count = (counts.get(member) ?? 0) + amount
@@ -395,24 +380,18 @@ export namespace Layout {
return {
name,
reads: (key) => readsByKey.get(key),
reads: dependencyMask(fields),
stage(key, value) {
const previous = byKey.get(key)
const trackedMembers = tracked(value, (candidate) => {
const source = extract(candidate)
return {
source,
members: source === previous?.source ? previous.members : new Set(source),
}
})
const source = extract(value)
const members = source === previous?.source ? previous.members : new Set(source)
return () => {
readsByKey.set(key, trackedMembers.reads)
if (!previous) trackedMembers.result.members.forEach((member) => adjust(member, 1))
if (previous && previous.members !== trackedMembers.result.members) {
trackedMembers.result.members.forEach((member) => !previous.members.has(member) && adjust(member, 1))
previous.members.forEach((member) => !trackedMembers.result.members.has(member) && adjust(member, -1))
if (!previous) members.forEach((member) => adjust(member, 1))
if (previous && previous.members !== members) {
members.forEach((member) => !previous.members.has(member) && adjust(member, 1))
previous.members.forEach((member) => !members.has(member) && adjust(member, -1))
}
byKey.set(key, trackedMembers.result)
byKey.set(key, { source, members })
}
},
applyDelta(key, change) {
@@ -425,25 +404,23 @@ export namespace Layout {
members.add(member)
adjust(member, 1)
})
// The set was mutated in place: poison the source-identity check and
// drop observed reads so the next non-delta projection recomputes.
// The set was mutated in place, so poison the source-identity check.
byKey.set(key, { source: members, members })
readsByKey.delete(key)
},
remove(key) {
byKey.get(key)?.members.forEach((member) => adjust(member, -1))
byKey.delete(key)
readsByKey.delete(key)
},
afterPlacement() {},
afterRebuild() {},
member: (candidate) => counts.has(candidate),
}
}
function firstEntry(name: PropertyKey, matches: (value: A) => boolean): Entry {
function firstEntry(
name: PropertyKey,
fields: readonly PropertyKey[] | undefined,
matches: (value: A) => boolean,
): Entry {
const matching = new Set<Key>()
const readsByKey = new Map<Key, number>()
let slot: Readable<A> | undefined
function findFirst() {
@@ -472,25 +449,23 @@ export namespace Layout {
return {
name,
reads: (key) => readsByKey.get(key),
reads: dependencyMask(fields),
stage(key, value, mode) {
const matched = tracked(value, matches)
const matched = matches(value)
return (valueSlot) => {
readsByKey.set(key, matched.reads)
const previous = matching.has(key)
if (matched.result) matching.add(key)
if (!matched.result) matching.delete(key)
if (matched) matching.add(key)
if (!matched) matching.delete(key)
if (mode === "add") return
if (slot === valueSlot && !matched.result) {
if (slot === valueSlot && !matched) {
findFirst()
return
}
if (slot !== valueSlot && !previous && matched.result) afterPlacement(valueSlot)
if (slot !== valueSlot && !previous && matched) afterPlacement(valueSlot)
}
},
remove(key, removedSlot) {
matching.delete(key)
readsByKey.delete(key)
if (removedSlot && slot === removedSlot) findFirst()
},
afterPlacement,
@@ -501,13 +476,13 @@ export namespace Layout {
const entries: Entry[] = Reflect.ownKeys(definitions).map((name) => {
const definition = definitions[name]
if (definition.type === "members") return membersEntry(name, definition.extract)
return firstEntry(name, definition.matches)
if (definition.type === "members") return membersEntry(name, definition.fields, definition.extract)
return firstEntry(name, definition.fields, definition.matches)
})
const byName = new Map(entries.map((entry) => [entry.name, entry]))
// Stage refresh work for one changed value: explicit member deltas win,
// then projections whose observed reads are disjoint from the change are
// then projections whose declared fields are disjoint from the change are
// skipped entirely. Returns undefined when no index work is required.
function stageRefresh(key: Key, value: A, mask: number, changes?: unknown) {
const record = changes as Readonly<Record<PropertyKey, MemberChange | undefined>> | undefined
@@ -519,14 +494,19 @@ export namespace Layout {
;(staged ??= []).push(() => delta(key, change))
continue
}
const reads = entry.reads(key)
if (reads !== undefined && (mask & reads) === 0) continue
if ((mask & entry.reads) === 0) continue
;(staged ??= []).push(entry.stage(key, value, "replace"))
}
return staged
}
function publishStaged(value: A, mask: number, staged: readonly Commit[] | undefined, slot: Readable<A>) {
function publishStaged(
previous: A,
value: A,
mask: number,
staged: readonly Commit[] | undefined,
slot: Readable<A>,
) {
// Indexes are plain data, not reactive state; the transaction exists so
// subscribers cannot observe published values with stale indexes. With
// no staged commits there is nothing to observe out of order.
@@ -538,16 +518,12 @@ export namespace Layout {
})
function publishValue() {
return withPending(mask, () => values.update(value))
}
}
function withPending<T>(mask: number | ReadonlyMap<Key, number>, f: () => T) {
pending = mask
try {
return f()
} finally {
pending = undefined
pending = { previous, next: value, mask }
try {
return values.update(value)
} finally {
pending = undefined
}
}
}
@@ -555,13 +531,13 @@ export namespace Layout {
...values,
set(next) {
const keys = next.map(plan.keyOf)
if (new Set(keys).size !== keys.length) throw new Error("Keyed values must have unique keys")
const retained = new Set(keys)
if (retained.size !== keys.length) throw new Error("Keyed values must have unique keys")
const existing = new Map<Key, A>()
values.slots().forEach((slot) => {
const value = slot()
existing.set(plan.keyOf(value), value)
})
const masks = new Map<Key, number>()
// Stage user-code projections for new and changed values only;
// retained equivalent values keep their index state and reads.
const staged: Array<{ readonly key: Key; readonly commits: readonly Commit[] }> = []
@@ -571,44 +547,52 @@ export namespace Layout {
staged.push({ key, commits: entries.map((entry) => entry.stage(key, value, "add")) })
return
}
const mask = plan.diff(existing.get(key)!, value)
masks.set(key, mask)
const previous = existing.get(key)!
if (previous === value) return
const mask = plan.diff(previous, value)
if (mask === 0) return
const commits = stageRefresh(key, value, mask)
if (commits) staged.push({ key, commits })
})
const retained = new Set(keys)
const structure = values.slots()
return Transaction.run(() => {
const changed = withPending(masks, () => values.set(next))
existing.forEach((_value, key) => {
if (!retained.has(key)) entries.forEach((entry) => entry.remove(key))
})
const changed = values.set(next)
if (!changed) return false
const structural = structure !== values.slots()
if (structural)
existing.forEach((_value, key) => {
if (!retained.has(key)) entries.forEach((entry) => entry.remove(key))
})
staged.forEach((item) => {
const slot = values.get(item.key)!
item.commits.forEach((commit) => commit(slot))
})
entries.forEach((entry) => entry.afterRebuild())
return changed
if (structural) entries.forEach((entry) => entry.afterRebuild?.())
return true
})
},
update(value) {
const key = plan.keyOf(value)
const slot = values.get(key)
if (!slot) return false
const mask = plan.diff(slot(), value)
const previous = slot()
if (previous === value) return false
const mask = plan.diff(previous, value)
const staged = mask === 0 ? undefined : stageRefresh(key, value, mask)
return publishStaged(value, mask, staged, slot)
return publishStaged(previous, value, mask, staged, slot)
},
modify(key, f, changes) {
const slot = values.get(key)
if (!slot) return false
const value = f(slot())
const previous = slot()
const value = f(previous)
// Publication below targets the slot for the value's own key, so the
// modify key-preservation invariant must be enforced here.
if (values.get(plan.keyOf(value)) !== slot) throw new Error("Keyed modify must preserve the value key")
const mask = plan.diff(slot(), value)
if (previous === value) return false
const mask = plan.diff(previous, value)
const staged = mask === 0 ? undefined : stageRefresh(key, value, mask, changes?.members)
return publishStaged(value, mask, staged, slot)
return publishStaged(previous, value, mask, staged, slot)
},
insert(value, position) {
const key = plan.keyOf(value)
@@ -618,7 +602,7 @@ export namespace Layout {
return Transaction.run(() => {
const slot = values.insert(value, position)
staged.forEach((commit) => commit(slot))
entries.forEach((entry) => entry.afterPlacement(slot))
entries.forEach((entry) => entry.afterPlacement?.(slot))
return slot
})
},
@@ -636,7 +620,7 @@ export namespace Layout {
if (!slot) return false
return Transaction.run(() => {
const moved = values.move(key, position)
if (moved) entries.forEach((entry) => entry.afterPlacement(slot))
if (moved) entries.forEach((entry) => entry.afterPlacement?.(slot))
return moved
})
},
@@ -712,7 +696,7 @@ export namespace Layout {
const factory = Function(
"custom",
`${emitter.declarations.join("\n")}\nreturn (left, right) => { let changed = 0\n${statements.join("\n")}\nreturn changed }`,
) as (custom: ReadonlyArray<Field<unknown>["equivalent"]>) => (left: A, right: A) => number
) as (custom: ReadonlyArray<Field<unknown>>) => (left: A, right: A) => number
return factory(emitter.custom)
}
@@ -792,8 +776,8 @@ export namespace Layout {
const fields = model.perVariant.get(name)
if (!fields) {
const variant = variants[name]
const index = emitter.custom.push((left, right) => variant.equivalent(left, right)) - 1
return `case ${label}: return custom[${index}](left, right) ? 0 : -1`
const index = emitter.custom.push(variant) - 1
return `case ${label}: return custom[${index}].equivalent(left, right) ? 0 : -1`
}
const statements = fields.map((field) => {
const fieldProperty = JSON.stringify(String(field.name))
@@ -804,17 +788,17 @@ export namespace Layout {
const factory = Function(
"custom",
`${emitter.declarations.join("\n")}\nreturn (left, right) => { if (left[${property}] !== right[${property}]) return -1; switch (left[${property}]) { ${cases.join("\n")}\ndefault: return -1 } }`,
) as (custom: ReadonlyArray<Field<unknown>["equivalent"]>) => (left: unknown, right: unknown) => number
) as (custom: ReadonlyArray<Field<unknown>>) => (left: unknown, right: unknown) => number
return factory(emitter.custom)
}
// Whole-tree generation: one Function() per plan whose source inlines struct
// comparisons, emits real loops for arrays, and hoists unions into named
// inner functions the engine can inline. User-supplied equivalence functions
// remain indirect calls through the `custom` array; everything structural
// compiles to direct code with no interior closure boundaries.
// Whole-tree generation: one Function() per plan operation whose source
// inlines struct comparisons, emits real loops for arrays, and hoists unions
// into named inner functions the engine can inline. User-supplied equivalence
// functions remain indirect calls through the `custom` array; everything
// structural compiles to direct code with no interior closure boundaries.
type Emitter = {
readonly custom: Array<Field<unknown>["equivalent"]>
readonly custom: Field<unknown>[]
readonly declarations: string[]
readonly names: Map<unknown, string>
}
@@ -849,7 +833,7 @@ export namespace Layout {
function assemble<A>(emitter: Emitter, body: string) {
const factory = Function("custom", `${emitter.declarations.join("\n")}\nreturn (left, right) => ${body}`) as (
custom: ReadonlyArray<Field<unknown>["equivalent"]>,
custom: ReadonlyArray<Field<unknown>>,
) => (left: A, right: A) => boolean
return factory(emitter.custom)
}
@@ -896,8 +880,8 @@ export namespace Layout {
})
return `${name}(${left}, ${right})`
}
const index = emitter.custom.push((left, right) => field.equivalent(left, right)) - 1
return `custom[${index}](${left}, ${right})`
const index = emitter.custom.push(field) - 1
return `custom[${index}].equivalent(${left}, ${right})`
}
function declareUnion(
+3 -3
View File
@@ -7,9 +7,9 @@ export function useValue<A>(readable: Readable<A>): Accessor<A> {
}
/**
* Reactive accessor for one keyed slot. Tracks structure only until the slot
* exists; afterwards value changes flow through the slot alone, so unrelated
* structural churn cannot re-render the consumer.
* Reactive accessor for one keyed slot. Structural changes re-resolve the
* slot, while memo equality prevents an unchanged slot from propagating to the
* consumer. Value changes flow through the slot itself.
*/
export function useSlot<A, Key>(keyed: Keyed.Keyed<A, Key>, key: () => Key): Accessor<A | undefined> {
const structure = useValue(keyed.slots)
+1 -1
View File
@@ -1,4 +1,4 @@
import { Keyed } from "effect-quark"
import { Keyed } from "@opencode-ai/quark"
import { createStore, produce } from "solid-js/store"
import { SessionTimeline, type PartRef } from "../src/routes/session/timeline"
import { createHarness, type Workload } from "../../quark/bench/harness"
+1 -1
View File
@@ -87,7 +87,7 @@
"clipboardy": "4.0.0",
"diff": "catalog:",
"effect": "catalog:",
"effect-quark": "workspace:*",
"@opencode-ai/quark": "workspace:*",
"fuzzysort": "catalog:",
"get-east-asian-width": "catalog:",
"open": "10.1.2",
+1 -1
View File
@@ -1,5 +1,5 @@
import type { SessionMessageAssistant } from "@opencode-ai/client"
import { Keyed, Layout } from "effect-quark"
import { Keyed, Layout } from "@opencode-ai/quark"
/**
* Stable per-part reactive slots for assistant message content.
+1 -1
View File
@@ -13,7 +13,7 @@ import {
Switch,
useContext,
} from "solid-js"
import { KeyedFor, useSlot, useValue } from "effect-quark/solid"
import { KeyedFor, useSlot, useValue } from "@opencode-ai/quark/solid"
import { SessionContent } from "./content"
import path from "node:path"
import { EOL, tmpdir } from "node:os"
+2 -2
View File
@@ -1,5 +1,5 @@
import { Keyed } from "effect-quark"
import { useValue } from "effect-quark/solid"
import { Keyed } from "@opencode-ai/quark"
import { useValue } from "@opencode-ai/quark/solid"
import { batch, createEffect, on, onCleanup, type Accessor } from "solid-js"
import { useData } from "../../context/data"
import { useClient } from "../../context/client"
+1 -1
View File
@@ -1,5 +1,5 @@
import type { SessionMessageAssistant, SessionMessageInfo } from "@opencode-ai/client"
import { Keyed, Layout, Transaction } from "effect-quark"
import { Keyed, Layout, Transaction } from "@opencode-ai/quark"
import { SessionContent } from "./content"
const PartRefLayout = Layout.struct({