Repository navigation
v0.5.0
Every package below changes its public types or adds API, so each takes a minor
while pre-1.0: foldkit-surface 0.2.0, foldkit-remote 0.3.0,
foldkit-remote-server 0.3.0, foldkit-remote-drizzle 0.3.0,
foldkit-mixins 0.3.0, foldkit-mixins-surface 0.3.0, foldkit-agent 0.3.0,
foldkit-agent-a2a 0.3.0, foldkit-agent-mcp 0.3.0, foldkit-agent-native
0.3.0, foldkit-sync 0.5.0, and foldkit-mirror 0.2.0 (its Contract output
changed). foldkit-agent-webmcp 0.3.0 and foldkit-mixins-ui 0.3.0 republish
only for their peer ranges. foldkit-durable does not republish.
Several of these changes exist so that user code, tests included, needs no type
casts: a cast in a test marked a gap in the API.
foldkit-surface (breaking)
- Projection metadata is open to any package. Surface declared Remote's
requirement types, so no other package could attach facts to a Projection
without editing Surface.Metadata.key<A>(name, { merge, summarize })gives a
package its own typed slot;Projection.metadatacarries the entries and every
combinator merges them per key. Entries are found by the key object, so two
copies of one package do not share them. Metadatais opaque. Only a key'sofand composition make one; its
entries are private and frozen, so a hand-built value has none and nothing can
mutate them.MetadataTypeIdmarks a value.- Removed:
Projection.requirements,Projection.connections, and the
matchingProjection.fromReaderoptions. Remote's requirement types moved to
foldkit-remote(below). Contract.requirementsandSurfaceInspection.requirementsare now
metadata: MetadataSummary[]({ name, entries }), soModuleand DevTools
show every package's entries. A hand-writtenContractliteral must spell
metadata: [].Module.toMarkdownrenders the column as
name: entries; name: entries, with|escaped.- Schemas are decodable codecs.
Projection.ModelandSurface.Paramsare
Schema.Codec<Value, unknown>, soSchema.decodeUnknownSync(projection.Model)
needs no cast, andSurface.Paramsis present, not| undefined, when the
Surface declares params (ParamsSchema).Projection.fromReaderand
Surface.make({ Params })refuse a schema that needs decoding services.
foldkit-remote (breaking)
- Remote owns its requirements.
Requirement,RelationRequirement,
ConnectionRequirementand theRequirement.merge*helpers move here from
foldkit-surface, stored underRemoteRequirementsandRemoteConnections.
Read them withrequirementsOf(projection)/connectionsOf(projection).
Windowis gone; it wasQueryWindow. Data.refresh(model, projection | surface)marks what a consumer already
declares as due again, fromupdate, instead of restating each request. It
returns the Model, with selected fields readingRefreshingand loaded
connections invalidated; theData.subscriptionsread entries refetch them,
so the data is requested once. Unobserved data is revalidated with
Remote.prefetchandRemotePolicy.networkOnly. A refreshed connection's
page replaces its pages, so removed and reordered items follow the server and
later pages are paged again; a read already in flight is restarted rather than
applied after the refresh. Refreshing what is already refreshing returns the
same Model.Data.subscriptionsreturnsSubscriptionEntries, with exact<key>.read,
<key>.liveandretainkeys, so a lookup needs no!.RemotePersistence.dehydratereturnsstringwhen nomaxBytesis given.Remote.Modelis a decodableSchema.Codec<RemoteModel, unknown>.
foldkit-remote-server
- No longer depends on
foldkit-surface;Requirementcomes from
foldkit-remote.
foldkit-mixins-surface (breaking)
SurfaceView.describereturnsmetadata: MetadataSummary[]in place of
requirements.SurfaceView.render(view, surface, params, root)renders a view for a
Surface's projection with an inert builder, for demos, tests and static output.
foldkit-agent (breaking)
Agent.whencompletes on state. A Message contract couples a capability
to one implementation path; a state contract completes when a condition holds,
whatever made it so. It reads aprojectionof the application's Model (which
must be that Model, and needshost.subscribe) or asource: { get, subscribe }outside it. Completed means the condition holds, not that this
call made it true; a creation still completes on its Message withcorrelate.
A throwing predicate fails the invocation as a defect, and a value a
notification left unchanged is not evaluated again; asourceis re-evaluated
on every notification, since it may mutate its value in place.requestis inferred inline inAgent.expose; insideAgent.variantit is
annotated and checked againstinput. Elsewhere it isunknown.CompletionOutcomeis a union, and so isDispatchResult.completion: a
Message contract carries itsmessage; a state contract has none and cannot
fail. Code that readcompletion.message._tagunconditionally needs?..Manifestcompletion gains akind:{ kind: 'message', success, failure? }or{ kind: 'state', observes }.AgentHost.dispatchmay return a failing Effect, such asSync.mount's
Exit; the failure is the invocation's defect, as a thrown error already was.- The audit log is typed by principal.
auditLog<Principal>,AuditSink,
AuditLogOptionsandAuditRecordtake the principal type, so a projection
(caller: User) => caller.idneeds no cast, and binding a log built for
another principal is refused. An unannotated projection now seesunknown,
and a hand-written sink declares its principal type.AuditRecord.principal
isPrincipal | undefined: a call refused before its principal is resolved
is recorded withundefined, and theprincipalprojection is not called. Agent.makereturnscontextas present when one was given.
Agent.contextSchemareturns aJsonSchemaDocumentrather than an untyped
record.
foldkit-agent-mcp (breaking)
AgentMcp.httpApp({ server })accepts a server alone; passing handler options
beside it, which were ignored, is now a type error.AgentMcp.stdiotypesinputandoutputas theon/off/writeit uses
(LineInput,LineOutput), so a fake stream needs no cast. Node's
process.stdinandprocess.stdoutstill fit.
foldkit-agent-a2a (breaking)
Success.resultis aTask, which every handler already returns. Code that
builds aSuccessby hand must pass one.
foldkit-agent-native (breaking)
AgentNative.actionsreturns anActionRegistrywith exact keys for a known
contract, so a known capability needs no!; indexing it with an arbitrary
string is a type error. An open contract keeps string keys.- An action's
schemaalso types its~standard.jsonSchema.
foldkit-agent-webmcp
- Republished with a peer range on
foldkit-agent0.3.0. Its own API is
unchanged, but the published 0.2.1 peersfoldkit-agent@^0.2.1, which in 0.x
excludes 0.3.0.
foldkit-sync (breaking)
- The committed state is readable.
Replica.committedand
ReplicaSnapshot.committedare the server-confirmed state the optimistic
sharedis built on: a pending edit reaches them only once committed, and a
rejected one never does. Mounted.committedis aCommittedView({ get, subscribe }), the source
an agent waits on to finish on confirmation rather than on its own optimistic
edit:Agent.when({ source: mounted.committed, … }). It is notified after every
exchange, including a checkpoint that keeps the cursor.Mountedgains a third
type parameter,Shared, defaulting tounknown.- An exchange that acknowledges an edit without returning it now re-installs the
shared slice, instead of leaving the dropped edit in the Model. - A Model, Message or committed listener that throws no longer stops the
listeners after it or the mount's refresh; its error is re-thrown from a
microtask. disposeno longer hangs when a persist dies with a defect.- A refresh no longer hides a durable edit still waiting for the replica.
An exchange that settled while an edit's submit waited for the replica lock
installed a shared slice without it, so the edit vanished until the next
exchange. The mount now submits one edit at a time, in dispatch order, and a
refresh replays the edits the replica does not hold yet on top of one replica
snapshot. Nothing is deferred, so remote changes still show while edits keep
overlapping, and no edit is applied twice. Replica.snapshotreads the status,shared,committedand the new
ReplicaSnapshot.nextLocalSequencefrom one replica state.DefinedSync.replay
is the reducer the replica replays durable Messages with.Sync.forApplication(App).make({ authorize })types
journalContract().authorizeas present.
foldkit-mirror
- Its
Contractcarriesmetadata: []in place ofrequirements: [].
foldkit-mixins
Attributes.find,filterandtagOfread a resolvedSlotAttributes
bundle by tag and return that variant typed (find(attrs, 'Class')?.value),
so reading attributes needs no cast. An unknown tag is a type error.SlotView.inertBuilder<Message>()is Foldkit'sinertHtmltyped for a
Message universe, for rendering outside a runtime. It holds the one cast the
invariant builder needs.
foldkit-mixins-ui
- Republished with a peer range on
foldkit-mixins0.3.0. Its own API is
unchanged, but the published 0.2.0 peersfoldkit-mixins@^0.2.0, which in 0.x
excludes 0.3.0.
foldkit-remote-drizzle
keysetWherereturnsSQLfor a non-empty tuple of order terms.