Repository navigation
Releases: doeixd/foldkit-plus
Release list
v0.25.0
Concentric corners: buttons and fields round with the Card, Dialog, Toast or Alert holding them
(Theme.concentric), the Drawer gains a grip to drag it by, and a Calendar, a Slider and
Steps no longer spill a phone's card. foldkit-mixins 0.15.0 and foldkit-mixins-ui 0.14.0
change, and foldkit-plus is 0.7.0. Packages that pin one of them, or a package that does,
republish with a patch so their ranges cover the new versions (foldkit-builder, foldkit-cms,
foldkit-cms-drizzle, foldkit-composition, foldkit-mixins-builder, foldkit-mixins-crud,
foldkit-mixins-data-grid, foldkit-mixins-form, foldkit-mixins-richtext,
foldkit-mixins-surface, foldkit-primitives). Nothing written for 0.24 needs changing; a
control in a card is drawn with a smaller corner (below).
Added
-
foldkit-mixins/theme'sTheme.concentric({ min, max }): a control's corner, concentric with
the nearest container thatnests(its corner less its inset), kept betweenminandmax,
andmaxoutside any.Theme.nestsnow also publishes--fk-control-cornerfor it. -
In
foldkit-mixins-ui, buttons, the segmented tray and every field's chrome take
concentric({ min: sm, max: md }), and the Card, Dialog panel, Toast and Alertnests, so the
buttons and fields in them round with their corner (smwhere the padding is deeper than the
corner, as in the default theme's cards).control-roundstill makes a button a pill. -
foldkit-mixins-ui's Drawer has agripslot: more of the panel to drag it by, such as its
row of tabs, where a vertical swipe drags and a sideways one still scrolls. A drag that starts
there ends with no click to pass over, so the next press on the handle toggles it.
Changed
- A button or a field inside a Card, Dialog, Toast or Alert is drawn with a corner concentric with
the container's instead ofradius-md: with the shipped scale,radius-sm. - The Drawer's handle is a 44px target, drawn tight to its bar by negative margins, and the
peek stays above a phone's home indicator (safe-bottom). - A Slider's mark labels at either end sit flush with the track's ends instead of centered on
their dots, hanging half past the track. - A horizontal Steps row narrower than 17rem draws the current step's title and description on
a line of their own under the markers, the lines between them even, instead of squeezing them
beside the current marker.
Fixed
- A Calendar's month in a container narrower than seven cells shrinks its days, which stay round,
instead of spilling past the container.
v0.24.0
Themes: hard shadows and a relief for buttons and fields (Theme.hardShadows, Theme.relief),
type knobs (text-scale, heading-scale, leading, the heading's weight and tracking), a pill
control corner, and a Drawer docked to the bottom of the screen. foldkit-mixins 0.14.0 and
foldkit-mixins-ui 0.13.0 change, and foldkit-plus is 0.6.0. Packages that pin one of them, or
peer on it, republish with a patch so their ranges cover the new versions (foldkit-cms,
foldkit-cms-drizzle, foldkit-composition, foldkit-mixins-builder, foldkit-mixins-crud,
foldkit-mixins-data-grid, foldkit-mixins-form, foldkit-mixins-richtext,
foldkit-mixins-surface, foldkit-primitives). Nothing written for 0.23 needs changing; a
Typography display is set a hair tighter (below).
Added
foldkit-mixins/theme'sTheme.hardShadows: the shadow scale drawn as solid offset copies
with no blur, for retro and brutalist themes. Compose it overTheme.tokens;
shadow-strengthstill fades it.- A control's relief:
Theme.tokensgainsshadow-raised,shadow-pressedand
shadow-field, flat by default, andfoldkit-mixins/theme'sTheme.reliefgives them depth
(soft,hard, orbevel's lit and shaded edges). Infoldkit-mixins-ui, a solid, outline
or primary Button stands out byraisedand is pressed in bypressedwhile held or on; ghost
and icon buttons stay flat; and every field's chrome (Input, Textarea, Select, Listbox,
Combobox, DatePicker, NumberField, TagsInput, OtpField, CascadeSelect, InputCopy, the
pagination's page size, Checkbox and RadioGroup) sinks byfield. - Type knobs in
Theme.tokens:text-scalescales every size of the type scale,
heading-scalehow far the headings stand above the body, andleadingthe line heights;
weight-headingandtracking-headingset what a heading is drawn in. At their shipped values
every size, line height and weight draws as before.foldkit-mixins-ui's Typography headings and interface roles, the Dialog's title and
the Empty title now readfont-heading,weight-headingandtracking-heading, so a theme's
heading font reaches them. foldkit-mixins-ui'sDrawerandRecipes.Drawer: a panel docked to the bottom of the
screen that peeks or opens beside the page, not over it, as a tool tray. Its handle drags it
(a flick or a quarter of the way settles it) and a click or Enter toggles it; the handle's bar
bends into a chevron pointing up as it goes down, with the finger while dragged.dock: 'always',
orbelow-lg/below-xl, above which it is the column it sits in.radius-controland thecontrol-roundknob: a Button and a Segmented tray round to a pill at
1, whatever the radius factor, while fields keepradius-md.
Changed
foldkit-mixins-ui: Typography'sdisplaytakes the heading tracking (-0.01emby default),
as the other headings already did.
Fixed
foldkit-compositionandfoldkit-mixins-surfacedeclared theirfoldkit-mixinspeer as
^0.11.0, which no release since 0.11 satisfies; they now peer on the version they ship with.foldkit-mixins: an element holding several Mounts no longer logs "unhandled failure" for each
as it is removed; a Mount that ends interrupted is torn down, not failing.
v0.23.0
Mobile and touch: hover that no longer sticks after a tap, a pressed state for every control,
44px targets on a touch screen, safe areas, the on-screen keyboard, holds and pinches, and
fixes for the Sheet's drag and the editor's caret keys. foldkit-mixins 0.13.0,
foldkit-mixins-ui 0.12.0, foldkit-primitives 0.12.0, foldkit-richtext-dom 0.6.0 and
foldkit-mixins-form 0.7.0 change; foldkit-mixins-data-grid 0.2.4 is fixes. Packages that
pin one of them republish with a patch to re-pin (foldkit-builder, foldkit-cms,
foldkit-cms-drizzle, foldkit-mixins-builder, foldkit-mixins-crud,
foldkit-mixins-richtext), and foldkit-plus is 0.5.0. Changes that can break code written
for 0.22: a Table without fluid is wrapped in its frame; the ImageCropper's zoom Mount is
ImageCropperZoom; the Menu's and the Toast's Models gain fields; and an editor edit that
changes nothing returns a RichText.answer Command, which a Scene test resolves.
Added
foldkit-richtext-dom/editor'sanswerEditor(hostId)and the attachment'sanswered():
the editor bundle answers every edit its editor asks for, once drawn or refused, with a
RichText.answerCommand. A Scene test that sends an edit which changes nothing now
resolves that Command, and an application that replaces the editor'sRichText.patch
with its own runs its patch before the editor's other Commands.foldkit-primitives/dom'sKeyboardInsetMount (andkeyboardInset,KEYBOARD_INSET): the
on-screen keyboard's height as--fk-keyboard-inseton its element, from the visual
viewport.foldkit-mixins-ui'sDialog.keyboardmixin runs it on a dialog's or sheet's
panel, and the Dialog and Sheet recipes keep the panel above the keys: iOS Safari, and
Chrome on Android, leave a fixed panel under them when a field in it takes focus.foldkit-mixins-form:FieldAttrstakesautocomplete,inputMode,enterKeyHint,
autocapitalizeandspellcheck, for text, multiline and number fields; anemail,telor
urltype turns capitalizing and spellchecking off by default.foldkit-mixins: asafetoken group,Theme.ref.safe.top/right/bottom/left:
what a notch, rounded corners or the home indicator take from each screen edge
(env(safe-area-inset-*), 0 unless the page's viewport meta saysviewport-fit=cover).
Changed
foldkit-mixins: a rule whose selector hovers is written for a pointer that can hover
(@media (hover: hover)), with a copy for one that cannot that keeps every other branch
(:is(:hover, :focus-within)still shows on focus there). A touch screen keeps:hoveron
what was tapped last, so a hover fill stayed behind after the finger left. Class names do
not change.foldkit-mixins:Defaults.resetdraws no tap highlight of the browser's own, sets
touch-action: manipulationon interactive elements (a second quick tap is not a zoom),
andtext-size-adjust: 100%in place ofnone, which also turned off the reader's own
text size.Defaults.linksandDefaults.controlshover only where the pointer can, and
show:active.
Fixed
foldkit-mixins: a control's words are no text to select:Defaults.resetsets
user-select: noneon buttons,summary, and the button, tab, option, menu item, switch,
checkbox and radio roles, so a long press on one no longer selects its label and offers to
copy it on a phone, nor a double tap a word of it. A tree's rows and a grid's cells keep
theirs.foldkit-mixins-ui: a Toast stack under a finger holds its countdown without spreading out
(a newtouchholder in its Model): a stack spread as a tap landed, or as a swipe began,
moved what the finger was on, and the focus Android gives a tapped action spread it too. Its
close and action buttons have a finger's target where the pointer is coarse.foldkit-mixins-ui: an ImageCropper zooms by two fingers, spread or pinched (Pinched): its
viewport took the page's own pinch away and gave none back. Its zoom Mount is now
ImageCropperZoom, for the wheel and the fingers both.foldkit-primitives:PointerDragdrags by touch. A finger's press is a drag once held
stillHOLD_TO_DRAG_MS(400ms), and one that movesHOLD_SLOP(8px) first is a scroll, so a
list of draggable rows still scrolls; once the drag is on, the page is kept still and
Android's own menu shut. Before, the browser took every finger's drag for a scroll and
cancelled it, so the Builder's palette, layers and canvas could not be dragged on a phone.foldkit-mixins-ui: a context menu opens on a touch screen. A finger or a pen held on
Menu.target's area forMenu.HOLD_MS(500ms) opens it under the finger, since iOS fires
nocontextmenu, while a finger that moves 10px is a scroll and opens nothing; Android's
owncontextmenufor the hold opens it once, under the finger, aspointTargetnow records a
finger's press. The Menu's Model gainsholdingandhold.foldkit-mixins-data-grid: the fill handle takes its drag on a touch screen (touch-action: none), where a finger panned the grid instead, and it and the column resize grip have a
finger's target there (44px and 24px) around what they draw.foldkit-mixins-ui: a Carousel's autoplay pauses while a finger is on it and goes on when
it lifts; a tap's emulatedmouseenterheld it paused until something else was tapped.foldkit-richtext-dom: a press on a block's handle is a drag only once it moves 5px
(DRAG_THRESHOLD): a tap's wander, or the page scrolling under a still finger, dropped the
block.foldkit-mixins-ui: a Combobox's and the Command's field ask a phone's keyboard not to
capitalize or correct what filters, and to show a search key (autocapitalize,
autocorrect,spellcheck,enterkeyhint).foldkit-mixins-ui: a Table always stands in itsframe, which scrolls sideways when the
table is wider than the screen (the default static header; a sticky one keeps sticking to
the application's scroller). Breaking for markup: a Table withoutfluidis now wrapped
in the frame'sdiv.foldkit-mixins-ui: scrolled to its end, a swipe on a Dialog or Sheet panel, a popup list,
the Command list or the Sidebar's nav no longer moves the page behind
(overscroll-behavior: contain); a popup list is at most 40% of the screen a phone's
keyboard leaves; menu, listbox and HoverCard panels are never wider than the screen.foldkit-mixins:user-selectis written with-webkit-user-selectbeside it, which iOS
Safari still reads alone, so a long press on a drag surface no longer selects its text there.
The recipes' drag surfaces (sliders' dials, the colour area, the cropper, the signature pad,
splitters, the toast's body, the Sheet's handle, the FloatingPanel's grips) also set
-webkit-touch-callout: none.foldkit-mixins-ui,foldkit-mixins-data-grid: the OTP input, the colour picker's fields
and the grid's cell editor are 16px or more on a touch screen, where iOS zoomed the page into
them.foldkit-mixins-ui: on a touch screen the recipes' controls are as large as a finger
needs, 44px:Defaults.resetsets--fk-touch-floorwhere the pointer is coarse, and a
control's height on the size ladder is at least that (buttons, icon buttons square, fields,
selects, tabs, segmented items, menu and list rows, accordion triggers, calendar days,
carousel, alert and chat actions). A desktop keeps the ladder's density, and a region that
must stay dense sets the floor to0px.foldkit-mixins-ui: Toast stacks and Dialog panels keep clear of the notch and the home
indicator: each edge a stack sits at is held off by the screen's inset there, and a panel's
margins and largest size leave every inset out.foldkit-mixins-ui: a tap shows. The recipes' hover is also their pressed state
(:is(:hover, :active)), as are the hover fills of menu, listbox and tree rows, nav and
sidebar items, accordion triggers, table sort buttons, steppers, tag removers and pressable
cards and items: with hover written only where a pointer can, a finger on one of them
shows it, and lifting takes it away.foldkit-mixins-ui: a Sheet grabbed while its Dialog was still opening (an animation
ending, aSucceededShowDialog) lost the drag, and its handle took no new press until the
pointer was let go. A drag now ends only when the Dialog opens or closes. (#186)foldkit-richtext-dom: a caret key pressed before an edit was drawn moved the caret
through the page as it was: three fast Enters and an ArrowUp went two lines up. Caret keys,
and edits typed behind them, wait until the edits before them are drawn. (#187)
v0.22.0
A component library worth building on, and a richer rich-text editor.
foldkit-mixins-ui 0.11.0 grows from the parity components to the whole UI
plan: the polish pass over every component (state colour, concentric corners,
the top layer, overlay scroll bars, right to left), the merges into one Alert,
Dialog, Sheet, Menu, Slider and date system, and chat (ChatMessage,
MessageScroller, Thinking Indicator, Thinking Steps, Questionnaire) and Zag's
long tail (Angle Slider, Timer, Marquee, Image Cropper, Signature Pad, QR Code,
Chart, Cascade Select, Floating Panel, Tour). foldkit-mixins 0.12.0 for the
state ink, nests/nested(), surface.field, the shadow and chart tokens;
foldkit-primitives 0.11.0 for ScrollBars, Swipe, ScrollSettle,
PointerPosition and TrackBox; foldkit-richtext 0.4.0,
foldkit-richtext-dom 0.5.0, foldkit-mixins-richtext 0.4.0 and
foldkit-richtext-markdown 0.3.0 for the editor's changes below;
foldkit-remote-server 0.15.0 for RemoteServer.memory's entities and
optional principal; foldkit-auth 0.1.1 and foldkit-ssr 0.7.1 for Fetch
Metadata on a request with no Origin; foldkit-mixins-data-grid 0.2.3 for
the motion tiers; and foldkit-plus 0.4.0. Every package that pins one of
them republishes with a patch to re-pin: foldkit-builder 0.3.3,
foldkit-cms 0.7.3, foldkit-cms-drizzle 0.6.6, foldkit-mixins-builder
0.3.3, foldkit-mixins-crud 0.7.3, foldkit-mixins-form 0.6.2,
foldkit-remote-drizzle 0.12.3, foldkit-richtext-code 0.2.1,
foldkit-richtext-code-shiki 0.2.1 and foldkit-site 0.4.1.
Changes that can break code written for 0.21 are marked "Breaking" in the
entries below; most are in foldkit-mixins-ui and the rich-text packages.
Added
foldkit-primitives/dom:TrackBox. A Mount that follows the element
named by itsdata-targetand writes its viewport box (and, asked, its
corner radius) as--fk-<name>-x,-y,-w,-hand-radius, telling
TrackedTarget { target, found }; the Tour's cutout now draws with it
(itsSpotlightMount is gone, its properties are--fk-tour-wand-h,
and its ring follows the target's corner).foldkit-mixins-ui: Calendar options, presets and a locale. The
fourth argument ofCalendar.toView,inputsandresolve
(CalendarOptions) takesweekNumbers(ISO weeks),caption: 'label'or
month and year selects (dropdowns, answered byCalendar.show),
pagingwithCalendar.alongsidefor two months side by side, and a
range'spreviewunder the pointer. Presets areCalendar.lastDays,
nextDays,monthOf,dayandpresetsView;Calendar.localeOf(tag)
reads upstream'slocalefrom Intl; the recipe takesoutside: 'hidden'.foldkit-mixins-ui: Tabs, Segmented, Breadcrumb and a navigation menu
with panels. Tabs take icons and counts (labelandiconslots, a
Badge after the words),overflow: 'scroll'(one line, its edges
fading),size: 'lg', a vertical tablist's rule at its inline end, and
closable tabs (TabsContext.close: a close button and Delete; answer with
Tabs.closing). A Segmented option takes anicon, alone with
iconOnly; the recipe addstray: 'underline',width: 'fill'andsize: 'lg'. Breadcrumb'scollapse: { max, menu }makes the ellipsis open a
Menu of the hidden steps (Breadcrumb.menuRows,Breadcrumb.chosen).
Nav.viewdraws a navigation menu whose triggers open panels of link
columns in the top layer, with the APG's disclosure-navigation keys, its
open panel inNav.init/Nav.update; the recipe addsindicator: 'pill'
and a column.foldkit-mixins-ui: Steps, Accordion, Table, Progress, Rating and
Resizable grow. Steps takemarker: 'dot',connector: 'progress'with
the view'sprogress, andsizeon the Avatar's steps. The Accordion
takesvariant(plain,bordered,separated,filled,ghost),
separators,indicator: 'plus',indicatorPosition: 'start',active
and its ownicon. Table sorts (sortKey,sort: { by, onSort },
aria-sort,Table.nextSort), chooses rows with checkboxes
(selection), opens a row's detail below it (expansion) and stands an
emptystate across itself, all the application's; the recipe adds
rows,densityandheader: 'sticky'. Progress takesshowValue, a
value of stackedsections, abufferandsegments, and the recipe
tone,stripesandsize. Rating takesprecision: 0.5, a read-only
fraction,onClear,showValue, and Home and End (Rating.Ends); a
read-only rating takes noonChange; the recipe addsglyphand an
errortone. Resizable collapses a panel (collapsible,collapsedTo)
on Enter or a double-click, nests either way, andhandle: 'grip'shows
a pill of dots.foldkit-primitives/dom:ScrollSettle, a Mount that reports the
item nearest a scroller's start once a scroll rests (scrollend, or a
quiet scroll where it is missing), right to left from the right edge.foldkit-mixins-ui: a custom Select, and Combobox chips, clearing and an
empty panel. A select drawn over Listbox:Listbox.partsdraws an
option's icon, label, description and check, and the trigger's value (or
its placeholder, muted) and chevron;Recipes.Listboxgainscheck
(end,start,none),trigger(outline,filled,ghost) and size
lg. The Combobox takeschips(each chosen value a chip in the field,
its remove button choosing it again, which a multiple combobox toggles
off),clearContentandclearLabel(a clear button while the field has
text) andempty(a polite panel in the list's place with nothing to list,
a loading line too);Combobox.partsdraws an option with the typed text
marked.ComboboxChipRenderandComboboxExtraInputsare exported.foldkit-mixins-ui: TagsInput'smaxand refusals, PasswordInput's
strength and rules, InputCopy's reset.TagsInput.init({ max })holds at
mostmaxtags; a commit that adds nothing, or not all of the draft, is a
Refusal(Repeat,Empty,Full), said in a politemessageslot
(refusalTextwords it) with a one-time shake thattravelstills.
PasswordInputtakesstrength(aMeter),requirements
(PasswordRequirements listed under the field, each read as met or not)
andcapsLock: { on, onKey }, a polite hint while Caps Lock is on, read
from each key bycapsLockOf.InputCopygains aModel(init({ resetAfter }), default 2000ms) andupdate:Copiedshows the value as
copied and startsReset, which sets it back to "Copy" unless a later copy
came first.foldkit-mixins/theme: shadows tinted byshadow-handshadow-c.
Theme.oklch({ shadow: { h, c } })sets two knobs every shadow reads,
defaulting to the neutrals' hue at twice their chroma, so shadows take the
page's hue rather than a neutral black.foldkit-mixins-ui: Card, Avatar, Separator, Skeleton, Spinner, Item and
Empty grow their looks. Card takeselevation(0to3, a step of
the surface ladder: lighter in a dark scheme, a contact and an ambient
shadow in a light one) andlift: 'hover'(a lift under the pointer,
timestravel);Card.overlayis the one stretched press control a
pressable card or Item draws. Avatar takessrc,initials,status
andstatusLabel;Avatar.group({ people, max, label, moreLabel })draws
overlapping avatars and counts the rest as one more (AvatarGroupSlots,
Recipes.AvatarGroup({ overlap: 'tight' | 'loose' | 'none' })), and
Recipes.Avatargains sizesxsandxl,shape(circle,rounded,
square),color: 'name'(a hue from the name) andring. Separator
takeslabelanddecorative, and the recipeline(solid,dashed,
dotted),thickness,spacingandinset. Skeleton gainslines,
avatarRow,cardandtableRow({ columns }), andanimation(pulse,
shimmer,none). Spinner takesshowLabel, and the recipekind
(ring,dots,bars,pulse,orbit),sizeandtone(accent,
neutral,current). Item is the row primitive:press,label,
selectedanddisabled,Item.groupfor a list (ItemGroupSlots,
Recipes.ItemGroup({ layout: 'divided' | 'separated' })), and the recipe's
variant(plain,outline,muted) andmedia(plain,icon,
image). Empty takesmedia(icon,illustration),frame(none,
dashed,filled),tone(neutral,danger) andsize.foldkit-mixins-ui: menu check rows, groups and shortcuts; Command pages
and hints; a Toast component. A Menu row may be aCheckRow(check: 'checkbox' | 'radio',checked): choosing it sendsSelectedand leaves
the menu open, the application keepingchecked. Any row may name a
group(a heading, and a separator between groups), ashortcut(keys
drawn as Kbd withkbd, named inaria-keyshortcuts) andtone: 'destructive'. A Menubar's menus take the same rows, andMenubar.init(ids, { isAnimated, isModal })passes both to every menu. A Command row takes
icon,description(matched by the query) andopensPage; the view
takespage: { trail, onBack }(Backspace in an empty field goes back),
loading,hintsfor a footer of keys andkbd;Command.hotkey(message)
is ⌘K or Ctrl+K anywhere, andRecipes.Command({ frame: 'bare' })sits in
a dialog.Toastis now a component:init,show,settle,dismiss,
updateandview. Toasts stack after Sonner's, spread out when held,
pause while the pointer or focus is on the stack or the page is hidden
(keeping the time left), show three at most, swipe away toward their
edge, and are spoken in a polite or, for an error, an assertive region.
settleturns aloadingtoast into its outcome, a promise toast; an
actionsendsActedwith its value.Recipes.Toast({ tone: 'rich' })
tints each ty...
v0.21.0
Authentication through Yielded Auth. foldkit-auth 0.1.0 is new;
foldkit-remote 0.18.0, foldkit-remote-server 0.14.0, foldkit-sync
0.12.0, foldkit-guard 0.3.0, foldkit-agent-mcp 0.7.0, foldkit-ssr 0.7.0,
foldkit-site 0.4.0 and foldkit-plus 0.3.0 for the changes below, and
foldkit-primitives 0.10.2 for a corrected doc comment; every package that
pins one of them republishes with a patch to re-pin.
Five changes can break code written for 0.20:
serveFetch's anddefineDocumentHost'sresolvePrincipalreturns an
Effect, refusing withRemoteUnauthenticated; a resolver that throws is a
500, where it was a 401 carrying its message.foldkit-agent-mcp'sauthenticatereturns an Effect refusing with
AgentMcp.unauthenticated(), where it returned the principal orundefined.Guards.declarerequiresunguarded: 'allow' | 'deny'.SSR.entrythrows for a plan with a server fallback and noorigins.Remote.httpreads every 401 asUnauthenticatedand every 403 as
Forbidden, whatever the body says, so a view that compared other tags for
them sees these instead.
Added
-
foldkit-sync/journal:documentHost, a document's Sync host for any
runtime. It answers an upgrade request before a socket opens: refused
with theResponseto send (401 for a refused principal, 500 for a
resolver, layer or journal that broke), or accepted withservefor the
opened socket andabandonfor one that never opens, withrevalidate,
opens,admitandsettleasdefineDocumentHosthas them, and
layerrequired exactly when the resolver needs a service.defineDocumentHostis now built on it,
and a runtime withoutWebSocketPairis a 500 that resolves no one. -
foldkit-auth: the client repeats whom it was answered as.
AuthServer.answered(config)(the resolver's config, with itskey) is
serveFetch'sheaders, naming each answer's principal in
x-foldkit-subject;believing()fromfoldkit-authis afetchfor
Remote.http/httpWithLivethat repeats it, so once another tab switches
account a read is refused withSUBJECT_CHANGEDrather than answered as
the new account. Any 401 makes it believe no one, and an answer to a
request sent before it (or beforeforget(), for this tab's own sign-in
and sign-out) is not believed. The subject is URI-encoded. -
foldkit-remote-server:serveFetchtakesheaders: principal => …,
added to every answer to that principal, a live stream's included. -
foldkit-auth(early, 0.1.0): a Yielded Auth session as the principal.
AuthServer.resolver({ auth, http, principal })fromfoldkit-auth/server
is aresolvePrincipalforserveFetch: it reads the session cookie Yielded
set (named from the sameHttp.makeoptions), verifies it with the
application'sAuthservice, and builds the principal with the
application'sprincipal. No cookie is a visitor; a cookie on a request
without a trustedOriginand Yielded's CSRF header, a cookie that no
longer verifies, two session cookies, or anx-foldkit-subjectheader
naming someone else is a 401Unauthenticated;httpnaming something
that is not an origin throws at once. Withrequests: 'browser'it serves
what a browser sends on the page's behalf,defineDocumentHost's upgrade
or a posted form, where a trustedOriginalone vouches for the cookie,
since neither carries a CSRF header;SUBJECT_HEADERandsubjectSaid,
fromfoldkit-auth, are what a client sends. Also re-exported as
foldkit-plus/authandfoldkit-plus/auth/server, an optional peer. -
foldkit-sync:defineDocumentHosttakeslayer: env => Layerfor its resolver.
resolvePrincipalmay need a service (a session store), whichlayer
provides; it is required exactly when the resolver needs one, built per
upgrade, kept while the socket is open forrevalidate, and closed at once
for a refused upgrade. -
foldkit-site:access, who may be shown a page. A node'saccess: { allow, otherwise }covers it and the nodes below it;
Site.accessOf(site, route, who)answersAccess.Allowed,Pending
whilewhois unknown, orDenied({ url })withotherwise's address for
what was asked. Presentation for the client and a server's 303; the server
guards the data regardless. Anotherwisewhose target the same rule
covers throws (a sign-in page mounted under it would loop with a growing
returnTo), and a rule in a node's options names its reader's type. -
examples/auth: Yielded Auth end to end. Password sign-in and
sign-out through Yielded's own client and routes, a guarded notes list
(an admin reads every note, a member their own, a visitor none),Mefor
"signed in as" and an admin line, and a change of principal carried to the
other tabs; a notes pageSite.accesssends a visitor away from, server
rendered as the request's reader; a Sync board whose socket is opened
with the session cookie, served on Node bydocumentHost; unit, mutation
and Chromium e2e tests, the e2e across two tabs and a server restart. -
foldkit-remote:RemoteData.denied(error), the denial a failure
was, aDenied(UnauthenticatedorForbidden, with the server's
reason; not a mutation'srefusal), typed,
or none for any other failure: aRemoteError's_tagis open, so a view
had to compare strings to tell "sign in again" from "could not load". -
foldkit-guard:Me, the visitor as an Entity.Guards.me(G, { interactions })declares one row,me, that everyone reads and no one
writes:signedIn,key(the principal's declaredkey) andmayfor the
named interactions;GuardServer.me(Me, Policy)serves it, counts for
validateas applying its rules, and refuses a Policy of another set when
it is made.Guards.anyoneis the rule everyone passes, a visitor
included, for a public surface underunguarded: 'deny': a surface naming
it restricts no one, for Sync documents and shared fields too, and
Guards.allnow takes at least one rule.Policy.keyisdeclare'skey
(G.key), for a seam holding the Policy.AuthServer.resolvertakes
key: Policy.keyto comparex-foldkit-subjectwith it. -
foldkit-ssr: a posted form's own resources.SSR.entrytakes
resources: request => LayerandSSR.handleresources: Layer, built
once for the post and provided to the Commands its fallback runs beside the
config'sresources; where both provide a service, the post's is used, so
a Command reads as who posted.SSR.entry'soriginsnames where a form
may be posted from: any otherOriginis a 403 before the body is read or
flagsandresourcesrun (Origin: nullcounts as the site's own when
Sec-Fetch-Sitesayssame-origin), and a plan with a server fallback
made into an entry withoutoriginsthrows, since a post runsflagsand
everyresourcesas whoever posted. -
serveFetchanddefineDocumentHosttakerevalidate: { every, key? }. While a live stream or a socket is open, the resolver is asked again,
of the request it opened with: a refusal ends the stream with
Unauthenticated(anevent: errorframe the client reads back as it) or
closes the socket with 4401, and a principal whosekey(asliveHub's,
Policy.keyOfwith guards) changed ends the
stream as a live error, or closes the socket with 1012, to be opened again
as the new principal. A revoked session stops being served within one
interval; the clients do not yet tell these closes apart, so a replica
reconnects after a 4401 too, and is refused at the upgrade. -
foldkit-guard:Policy.provide(context), a Policy whose bodies have
the services they need provided, for the read seams
(GuardDrizzle.visible,GuardServer.memory,GuardSync), which take a
Policy needing nothing more. A seam built from it counts as built from the
Policy it came from, forGuardServer.validate. -
foldkit-remote:RemoteUnauthenticated(_tag: 'Unauthenticated',
with an optionalreason), the refusal of a caller who could not be told,
besideRemoteForbidden;RemoteJsonAnswercarries it as
unauthenticated: { reason? }.foldkit-remote-server's
RemoteServer.unauthenticated(reason?)makes one. -
foldkit-remote: every call kind fails withUnauthenticatedon a
401. Reads, queries, mutations and live streams declare it in their RPC
errors andRemoteClient's types;Remote.httpandliveFetchread a 401,
Remote's own (with itsreason) or one from middleware in front of it, as
it, andData.mutation,ReadFailedandQueryFailedcarry
error._tag: 'Unauthenticated'.Data.satisfyandprefetchcan fail with
it, for a server render to answer as it answers a lost session.
foldkit-remote-server'sanswersends a handler's as a 401, and
serveFetchends a live stream with it as anevent: errorframe the
client reads back.
Changed
-
foldkit-agent-mcp:authenticateis an Effect.authenticate: request => Effect<Principal, Unauthenticated>, so it may ask a token store;
where it returnedPrincipal | undefined. Failing with
AgentMcp.unauthenticated(reason?)(or Remote'sRemoteUnauthenticated,
of the same shape) is a 401 carryingreason; a defect or a throw is a 500
that says nothing to the caller and is logged. Wrap
a plain lookup:request => Option.match(lookup(request), { onNone: () => Effect.fail(AgentMcp.unauthenticated()), onSome: Effect.succeed }). -
foldkit-guard:Guards.declarerequiresunguarded. What a guarded
Entity's surface with no rules decides is written down rather than
defaulted to'allow'. Passunguarded: 'allow'to keep what an omitted
one did;'deny'refuses such a surface. Anything else, an omission from
a caller without types included, throws atdeclare. -
**`...
v0.20.0
foldkit-entity 0.10.0, foldkit-form 0.6.0, foldkit-bundle 0.8.0,
foldkit-surface 0.10.0 and foldkit-remote 0.17.0 for the changes below;
every package that depends on one of them republishes with a patch to re-pin.
foldkit-metadata and the three rich-text model packages are unchanged.
Three changes can break code written for 0.19:
- A predicate or ordering term over another Entity than the query's no longer
compiles. - An
ownupdate in an assembly withData.wiringno longer receives Remote's
Messages, so arms that handled them are excess. - A test that matched a form's check as an instance by
args: { key }matches
form.Checkinstead.
Added
foldkit-entity: a predicate over another Entity is a compile error.
EachExprconstructor's result carries the names of the Entities its
fields belong to (Reads<Names>, type-only), andQuery.whererefuses a
query from another:a predicate reads Comment, but the query is from Post.
Order.asc,descandchosendo the same forQuery.orderBy. The
run-time check stays for a predicate typed plainly asPredicate. New
public types:Reads,ReadsOf, andInvalid, which declarations naming
these steps reach.foldkit-form:form.Check, the Command a key's check runs as. A
definition (Command.define('<form>.check', …)) rather than an inline
Command, so a test resolves it withCommand.resolve(form.Check, …)instead
of hand-building{ name: '<form>.check', args: { key }, effect }. Its args
now carry the draft, the values and the subject too, so a test that matched
the old Command as an instance byargs: { key }matches it no longer: match
form.Check, which compares by name.foldkit-form:Form.input(schema), a plain form's input. A form that
writes no Entity (a sign-up, a search, a login) tookEntity.defineand
Entity.inputover the same schema;Form.make('Search', Form.input(Search))
is that in one call. The ports of Foldkit's form, auth and job-application
examples use it.
Changed
foldkit-bundle: a wiring's own tags leave the parent's own update.
Wiringtakes aClaimsparameter typinghandles, and
assembly.update(own)handsowna Message without the tags a wiring claims
and does not share. Remote's wiring claims everyRemoteMessagetag, so an
ownno longer needsif (Remote.reduces(message))to narrow them away, and
one that matches exhaustively fails to compile whenData.wiringis removed
fromassemble. Anownthat handled Remote's tags itself now gets none.foldkit-entity:Expr.eqwith a plain value on the left says to put the
field there. The right side is typed from the left, soExpr.eq(3, field)
was refused with'number' is not assignable to 'Operand<any>'; it now
readsExpr.eq takes the field on the left and the value on the right.
Fixed
foldkit-form: a filled optional key not checked yet is refused, not left
out.engine.valuecounted an optional key whose draft was filled by
fillbut not yet validated as valid, and gave the value without it. Such a
draft must now pass its own check. A submit was never affected: it validates
every unchecked key first.
v0.19.0
Every package with a Foldkit peer now asks for Foldkit 0.168, so each of them
takes a minor version, as do foldkit-entity 0.9.0 (and, or, not and
comparisons), foldkit-remote-drizzle 0.12.0 (compiling them),
foldkit-guard 0.2.0, foldkit-durable 0.9.0 and foldkit-react-codegen
0.3.0 (TypeScript 6 as well as 5). Republished to re-pin: foldkit-cms-drizzle
0.6.3 and foldkit-remote-server 0.13.3; foldkit-richtext-markdown 0.2.1
widens a dependency. foldkit-metadata, foldkit-richtext,
foldkit-richtext-code and foldkit-richtext-code-shiki are unchanged.
Three changes can break code written for 0.18:
- Foldkit 0.168 itself: an application moves with it, renaming what 0.166 and
0.167 renamed (readfortoChildModel,persistentEntry, theDom
stream helpers). GuardServer.validaterefuses at startup a query body that filters or orders
by a member with a Read rule.- A Behavior whose Mount fails no longer takes the element's other Behaviors
with it; it is reported and the rest go on.
Added
-
foldkit-mixins: a reader's light or dark, before the first paint.
Theme.schemeScript(key?)is a head script that applies the scheme kept in
localStorage;Theme.chooseScheme(scheme, key?)applies and keeps one
('system'removes it);Theme.currentScheme()reads it back. The docs site
has an Auto/Light/Dark choice built on them. -
foldkit-sync: the browser asks before leaving while an edit is unsaved.
Sync.mountshows a durable edit before it is stored; a reload in between
lost it. From the first such edit until the last is stored,beforeunload
asks the reader first. -
Public types a dependent's declarations need. A library built on these
packages could not emit declarations under TypeScript 7 for a value whose
inferred type reaches a type it cannot name. The package indexes now export
them:foldkit-mixinsAttributeName,EventName,RequirementName,
HiddenOfandSlotRecipe;foldkit-bundlePlacedBy,CollectionBy,
WithoutResourcesandRuntimeResult;foldkit-agentCapabilitiesByName,
CapabilitiesByTag, theirAnyforms,DefinitionOfandAnyCompletion;
foldkit-data-gridEditableId. An interface that shares its namespace's
name is exported as a shape:CapabilityShape,SlotViewShape,
BundleShape,JournalShape. -
foldkit-guard:GuardServer.validaterefuses a query body that filters
or orders by a member with a Read rule. The row comes back without the
member, but which rows match, and in what order, still tells what it holds;
with comparisons a client could bisect it (cost < $max). -
foldkit-entity:Expr.and,Expr.orandExpr.not. The connectives,
with SQL's three-valued meaning:ortakes true over unknown,andfalse,
andnotof an unknown stays unknown, so a null matches neither a
comparison nor itsnot.andandorneed an operand and flatten one of
the same kind.Expr.whenGiven(input, given => predicate)is a filter that
applies only when a nullable input is given.evaluateruns them, and
decided(predicate, input)is a predicate's truth from the input alone,
when it reads no field. The conformance suite covers them. -
foldkit-entity: comparisons,Expr.lt,lte,gtandgte. Numbers
compare as numbers; text only underCollation.binaryorasciiFold
(declared on the field or passed) and against a field, refused where it is
written otherwise, since backends order text by different defaults. A value
compares in its encoded form, so a date stored as an ISO string is text. The
evaluator compares asorderBydoes, and the conformance suite covers them. -
foldkit-remote-drizzle: comparisons compiled, the column first however
the body wrote it, and text under its collation as keyset paging applies one
(collate "C"on Postgres,collate binaryornocaseon SQLite). -
foldkit-remote-drizzle:and,orandnotcompiled, and an operand
that reads no field decided per request, sowhenGivencompiles totrue
when its input is null and to the predicate alone when it is given, with no
parameter for the check.
Changed
- TypeScript 7. The workspace typechecks and builds with TypeScript 7.0.2,
the native compiler (a forced typecheck takes under a minute). Its DOM
library declares the Navigation API, sofoldkit-primitivesno longer
declares its own.foldkit-react-codegenpeers ontypescript@>=5.7.2 <7
rather than exactly 5.9.3: it reads source through the compiler API, which
TypeScript 7 does not ship. - Foldkit 0.168. Each package with a Foldkit peer asks for
foldkit@^0.168.0(and
foldkit-mixins-uion@foldkit/ui@^0.168.0), since they use what 0.166 and
0.167 renamed: a lifted child is read as anOption(read, not
toChildModel), the entry factories end inEntry
(Subscription.persistentEntry,Port.subscriptionEntry), and the DOM
Stream helpers areDom's (Dom.streamFromEventFilterMap,
Dom.streamFromKeyBindings;KeyBindingandKeySequencecome from
foldkit/dom). The examples build with Vite 8 and@foldkit/vite-plugin
0.27. - Effect 4.0.4. The workspace builds and tests against
effect@4.0.4and
its@effect/*packages at the same version. Published packages still peer on
effect@^4.0.0, andfoldkit-durablenow depends on
@effect/sql-sqlite-node@^4.0.0rather than exactly4.0.0, so an
application chooses its 4.x.examples/livestoremoves to
@livestore/livestore0.5.0-dev.1, which peers oneffect@^4.0.3, so the
workspace holds one Effect. foldkit-mixins: one Behavior's failing Mount no longer stops the others
on its element. The resolver merges an element's Mounts into one, and from
Effect 4.0.2 a merge ends when any stream in it fails, so aMeasurewithout
aResizeObservertook the canvas'sTargetswith it. Each fails alone now,
reported as Foldkit reports a Mount ([OnMount Name] unhandled failure).
Fixed
foldkit-remotedepends onfoldkit-metadata, which its declarations
name (import("foldkit-metadata").MetadataKey). Without the dependency the
declaration build copied metadata's types into remote's; TypeScript 7
refuses that, and the copy was a second set of the same types.foldkit-sync:foldkit-remoteis an optional peer again. The manifest
declaredpeerDependenciesMetatwice, and the second, naming only
foldkit-durable, replaced the first.foldkit-durable: a database it cannot open fails as aJournalError.
From@effect/sql-sqlite-node4.0.1 opening the file can fail with a
SqlError, which escaped the journal's declared errors.foldkit-mixins:Theme.oklch's doc said a knob overridden on any
subtree re-derives the palette. A custom property resolves where it is
declared, so tokens on:rootreach a subtree already resolved; the knob
re-derives them only on the element that declares them (:root, a
Theme.scopedselector, or a subtree that declares the theme itself).
v0.18.0
foldkit-site 0.2.0, foldkit-remote 0.15.0, foldkit-primitives 0.9.0,
foldkit-mixins 0.10.0, foldkit-mixins-ui 0.9.0, and foldkit-builder and
foldkit-mixins-builder 0.2.0 for the changes below, and foldkit-plus 0.1.0,
published for the first time. Republished to re-pin: foldkit-cms and
foldkit-cms-drizzle 0.6.2, foldkit-crud 0.7.2, foldkit-data-grid and
foldkit-mixins-data-grid 0.1.4, foldkit-mixins-crud 0.6.6,
foldkit-mixins-form 0.5.2, foldkit-mixins-richtext 0.2.4,
foldkit-remote-drizzle 0.11.2, foldkit-remote-server 0.13.2 and
foldkit-sync 0.10.2, each of which pins a bumped package. Every other
package is unchanged.
Two changes break code written for 0.17 (under Changed): a Site node's
shortcut is now a KeySequence, and a Builder's keys go through
keyPressed.
Added
-
foldkit-remote:Data.preload, prefetch fromupdate.satisfyas a
Command for a destination the reader has not reached (a hover over a link):
it reads what the given Surfaces read in the Model you give it and yields one
new Message,Preloaded, carrying the Messages that wrote it, so they reduce
into the live Model as it is by then. It never fails, and refuses a Surface
of another application when the Command is made.Remote.foldlifts it. -
foldkit-site: head metadata from the tree. A node'smeta(a function
of its route and the Model, returningfoldkit-ssr'sMeta) andindexed
flag, onSite.routeor theSite.nodebuilder.Site.metaOf(site, route, model)merges the route's chain root first, a deeper node's field replacing
its ancestor's, the preview title falling back to the node'stitle, and an
unindexed routenoindex; it is an SSR plan'smetaas it stands.
Site.indexedOfreads the flag, which a node inherits from the nearest
ancestor that declares it.foldkit-ssris now a dependency, for the type. -
foldkit-site: every address, and both directions, from the tree.
Site.paths(node, list)lists a node's addresses from an Effect (with when
each last changed), andSite.targets(site, paths)lists the whole site in
tree order for a build, a sitemap or prefetch, refusing a node with params
left unsaid. A tree's type now carries its routes:Site.parse(site, NotFound)is the application's URL parser andSite.hrefOf(site, route)
builds an address back, refusing a route no node holds.
Site.routeAt(site, url)is the route at an address, orNonewhere no
node takes it. -
foldkit-site:Site.shortcuts, key bindings from the tree.
Site.shortcuts(site, toMessage)is every node's shortcut as a
Subscription.keyBindingsbinding sending the Message made of its
landing target, refusing a shortcut with no landing or keys used twice. -
foldkit-primitives:pointedLinks()infoldkit-primitives/dom, the
address of each same-origin link the reader points at or tabs to, once
while they stay on it: the trigger for reading a page ahead. -
foldkit-site:Site.routing'sarrived. What the application does
once a new route is set and its routed pages are told, such as moving
focus to what it drew; an echo of the shown address runs nothing. -
foldkit-plus: every package in one install, andnpx foldkit-plus setup.
Each package's entry points are re-exported by subpath (foldkit-remote/port
isfoldkit-plus/remote/port), the namespaces most applications use from the
root, and an adapter that needs a library of its own (React, Drizzle, Shiki,
@foldkit/ui, Agent Native) is an optional peer, so none of those libraries
is installed with it.setupprints, for the project it runs in, how to
install Effect, Foldkit and Foldkit Plus with that project's package manager,
whicheffectorfoldkitversion is wrong or missing, the agent skill, and
which package does which job, written for a coding agent to follow. -
foldkit-mixins: code colors inTheme.oklch. Asyntaxgroup colors
each kindfoldkit-richtext-codeandfoldkit-richtext-code-shikiname
(syntax-keywordreads--fk-syntax-keyword), in hues the palette already
has, readable in both schemes;syntaxChromasets how colorful. -
foldkit-mixins-ui:CodeBlock. Code with a title or language, line
numbers and a copy button, its tokens (afoldkit-richtext-codetokenizer's
{ from, to, kind }) drawn by kind and colored byRecipes.CodeBlockfrom
the theme'ssyntaxgroup. The block's text is the code exactly.
Changed
foldkit-builder: a shortcut is judged inupdate. The layers and the
canvas sendKeyPressedfor every key a command binds
(PageBuilder.keyPressed), andupdaterunskeyCommandagainst the Model
as it is: a key pressed before the frame that drew a new selection deleted
nothing, or the node selected before it. A bound key with nothing to do
now has its default held back on those two. A view drawing a Builder by hand
attacheskeyPressedwhere it attachedkeyCommand, and a Builder handed
tofoldkit-mixins-builder's views must offerkeyPressedtoo.foldkit-site:Site.routingloads a link its tree does not hold. A
tree is one application's document, so a click on an address of this
origin that no node takes is a full load, where it was a Navigate Command
that could only draw the application's NotFound.foldkit-site: a node'sshortcutis a FoldkitKeySequence. Write
a sequence as an array (['G', 'H'], where it was'GH') and one press as
a string ('/','Mod+K'), asSubscription.keyBindingsnames keys.
v0.17.1
foldkit-entity 0.8.1, foldkit-remote-drizzle 0.11.1 and
foldkit-mixins-form 0.5.1 for the fixes below. Republished to re-pin:
foldkit-builder and foldkit-mixins-builder 0.1.2, foldkit-cms and
foldkit-cms-drizzle 0.6.1, foldkit-crud 0.7.1, foldkit-data-grid and
foldkit-mixins-data-grid 0.1.3, foldkit-form 0.4.3, foldkit-guard 0.1.1,
foldkit-mixins-crud 0.6.5, foldkit-remote 0.14.1, foldkit-remote-server
0.13.1 and foldkit-sync 0.10.1, each of which pins a bumped package. Every
other package is unchanged.
Fixed
foldkit-mixins-form: a locked key is posted with scripts off. It was
drawndisabled, and a browser leaves a disabled control out of a form post,
so a locked required key was missing and the save failed to decode; its
value is also drawn as a hidden input.foldkit-entity,foldkit-remote-drizzle: NUL is refused ineqand
in, as incontains. Given text holding one, the evaluator and SQLite
answered while Postgres failed the request; every interpreter now refuses it
as the query's own error.
v0.17.0
foldkit-agent 0.7.0; foldkit-bundle 0.6.0; foldkit-cms 0.6.0;
foldkit-cms-drizzle 0.6.0; foldkit-composition 0.2.0; foldkit-crud 0.7.0;
foldkit-durable 0.8.0; foldkit-entity 0.8.0; foldkit-mixins 0.9.0;
foldkit-mixins-form 0.5.0; foldkit-mixins-ui 0.8.0; foldkit-primitives
0.8.0; foldkit-remote 0.14.0; foldkit-remote-server 0.13.0;
foldkit-remote-drizzle 0.11.0; foldkit-ssr 0.5.0; foldkit-surface 0.8.0;
foldkit-sync 0.10.0; foldkit-data-grid and foldkit-mixins-data-grid 0.1.2.
New: foldkit-guard and foldkit-site 0.1.0. Republished to re-pin:
foldkit-agent-a2a, foldkit-agent-mcp and foldkit-agent-native 0.5.2,
foldkit-agent-webmcp 0.6.1, foldkit-builder and foldkit-mixins-builder
0.1.1, foldkit-bundle-surface 0.3.2, foldkit-form 0.4.2, foldkit-mirror
0.5.2, foldkit-mixins-crud 0.6.4, foldkit-mixins-richtext 0.2.3,
foldkit-mixins-surface 0.5.3 and foldkit-richtext-dom 0.3.1, each of which
pins a bumped package. Every other package is unchanged.
Who may do what is declared once, and Remote places rows instead of
refetching them. foldkit-guard declares guards on the shared domain and
enforces them on every seam: reads compiled into SQL, declared writes and
handlers checked before they land and answered as their writer may see the
row, Sync's cell edits, live updates and agents, with a startup check that
refuses a Source left unguarded. A write is declared as data (Write.update,
insert, delete) and served with no handler. A list whose query body
declares its order has a change placed in it with no request, checked against
the server under verifyPlacement. foldkit-site states a routed site's
topology once. And a review of this release's work fixed the ways a write or a
read could get past its rules, lose another author's edit, or send the server's
SQL to a client.
Upgrading from 0.16
journalExchange'srefuseisadmit(foldkit-sync), which answers as
authorizedoes and may be anEffect;opensrefuses a connection whole.
defineDocumentHosttakes both as functions of the principal, and its
settleis(env, journal) => Effect.foldkit-sync/entity's overlay helpers are gone (overlay,held,
newlyHeld,settled,shows):RemoteEditsfromfoldkit-sync/remote
shows the edits through Remote.Remote.visibleItemsisData.visibleItems(remote, connection)
(foldkit-remote), which draws a list as its reads do.- An
OrderTermis taggedTerm, and a body'sorderByholdsOrderings
(foldkit-entity): an interpreter resolvesQuery.orderFor(body, input)
first.foldkit-remote-drizzlerefuses anorderBybeside a body that
declares its order. Cms.slugTakenand theCmsConflict:/CmsSlugTaken:message prefixes
are gone (foldkit-cms): read a refusal withData.refusal
(Cms.refusal.field,Cms.refusal.conflict).CmsServer.maketakes a guard set (foldkit-cms-drizzle):policyand
authorsforisAuthor,transitionsbesideallow, and eachcontent
entry its guardedentity;published(column, isAuthor)is gone, bind with
GuardDrizzle.visible.Transactionmay fail with aRemoteServerError.LiveHub.deletedmay fail and needs the Sources' services
(foldkit-remote-server), aschangeddoes; it re-reads each subscriber's
selection rather than deleting for all.returning.rowfails with aRemoteServerError(foldkit-remote-drizzle)
when the database refuses it; run a handler's own statements through
runStatement.
Added
foldkit-remote-server: fields every principal reads alike skip the live
hub's re-read. A Source declaresshared(RemoteServer.entity,
RemoteServer.memoryper entity,foldkit-remote-drizzle'ssource);
hub.changed(ref, fields, values)sends a shared field's given value to
each subscriber as it is and re-reads the rest, and the handlers give it a
declared write's answer, the row as stored (a handler's answer is still
re-read). A shared field the Source does not serve is refused when it is
made.foldkit-guard:GuardServer.validaterefuses a shared field a Read
rule could tell readers apart by.- One editor for create and edit.
foldkit-entity: anOption-typed
input key maps itself to the field its value fits, as a nullable one does,
so an input's id may beSchema.OptionFromNullOr(PostId).foldkit-crud's
editor then saves a blank form withOption.none()and an opened one with
its id.foldkit-guard:GuardServer.mutation'sidmay answer an
Option, andnoneis a create, checked against the row'sCreaterule. foldkit-agent:authorizemay refuse with a reason. It answers
{ allowed: false, reason }(anAuthorizationDecision) beside a boolean,
and the reason is theAgentAuthorizationError's message.
foldkit-guard:maycarries explained refusals. Its answer gains
reasons, the sentence anexplaingave each row interaction it refused,
read withMay.reason;GuardAgent.authorizerefuses with it, so an
agent learns why as a client's write would.foldkit-entity,foldkit-remote: a list is fetched, not placed, when
a row changes what the server's read rule filters on.ReadFilter.of(members)
is Entity metadata naming the members a server filters rows by under a rule
the client does not run;foldkit-guard'sattachdeclares it from the
row's Read rules. Remote gathers it per Entity name when the domain is
defined (registry.filtered), so a query declared over the Entity before
the rule was attached still sees it, and a row a list does not hold that
changes one invalidates the list. A row it holds is judged by the body as
before.foldkit-remote-drizzle: avisiblerule may answer with anEffect,
and is given its own binding.(principal, self) => SQL | undefined | Effect<SQL | undefined, RemoteServerError>, so a rule that must ask
something first (a guard's body) can, and one written beforebindnames
columns throughself.columns. Every path a table is read by applies it as
before.foldkit-remote-drizzle:source(binding, { filter }), the
per-record read planRemoteServer.entitytakes, passed through. A source
and aquerycarry their binding'svisible, for a startup check.foldkit-agent:withPrincipal(key), the principal's type taken from
the Context key a server reads it from (G.Principal), so the agent's and
the server's are one type.withPrincipal<P>()stays.foldkit-crud: an editor locks the keys its row's reader may not set.
Crud.editor(name, { form, mutation, locks })loads the memberslocks
selects beside the form's, andeditor.at(...).locked(model)names the
form's keys whose member (or relation)locks.lockedrefuses, for
foldkit-mixins-form'slocked. Nothing is locked for a new one, or
before the row is loaded.foldkit-guard'sMay.locksis one.foldkit-mixins-form: keys drawn but not editable. The view input
locked: ['ownerId']draws those keysdisabled, every control kind (a
many picker's choices included), for a key the person may not set. A key in
a nested row is not locked by it, and a Bundle-backed key draws as its
Bundle does.foldkit-remote,foldkit-remote-server: a refusal of who is asking,
end to end. A mutation Source fails withRemoteServer.forbid(reason?);
the wire carries it asRemoteForbidden(_tag: 'Forbidden',reason
only when given),RemoteServer.answersends it as a 403 with
{ error, forbidden: { reason? } }, andRemote.jsonreads it back. A 403
whose body is not Remote's answer, from middleware in front of the server,
fails a mutation asForbiddenand any other call with the status.
Data.mutationisFailedwith theForbiddenerror, andRemoteError
carriesreason.foldkit-remote-server: a principal read from Context on each call, and
a live hub keyed by person.RemoteServer.handlers(server, principal)
takes a Context key forprincipalas well as a value, read by every read,
query, mutation and live subscription, so one handler set (one RPC layer)
answers every request and authentication provides the key per request; the
key is added to what the handlers require.RemoteServer.liveHub(sources, { key })shares a re-read between subscribers whose principals have one
key, read as the first of them; without one, only the same value shares.foldkit-remote-server,foldkit-remote-drizzle: a write answered as its
writer may now see the row.EntityWriter.read, when a writer has one,
reads a row as a principal may see it;RemoteServer.writeanswers through
it, and a row the write hid from its writer answers indeleted. A Drizzle
writerover a binding with avisiblerule reads through it. A writer may
fail withRemoteForbidden. A declared write's Source carrieswrites,
the Entity and the writer, for a startup check.foldkit-remote-server:EntitySource.visibleand
QuerySource.visible, the rule that decides which rows a source returns,
as it was given one. The server never runs it; a startup check such as
GuardServer.validatereads it.RemoteServer.memory's sources carry its
readable.foldkit-remote-server: what a principal sees, per record and per row.
EntitySource.filter(principal, fields)returns a read plan: the fields to
read (the allowed ones, and any the decision needs) andapply, one record
as the principal sees it; a member it drops is settled like one the Source
did not return. It runs for reads by id and live patches.RemoteServer.memory
takesfilterper entity, as it takesauthorize, andreadable(given a
lookup of another row by id), which
hides a row the principal may not read from reads by id, from every query,
and fr...