Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
297 commits
Select commit Hold shift + click to select a range
5213b43
fix(reference-host): allow empty document bodies
seonghobae Aug 20, 2026
db11957
test(reference-host): allow empty proposal replacement
seonghobae Aug 20, 2026
7ec8541
fix(reference-host): permit empty proposal replacement
seonghobae Aug 20, 2026
8e49936
test(reference-host): reject empty autosave validators
seonghobae Aug 20, 2026
0cc48b6
fix(reference-host): reject empty autosave validators
seonghobae Aug 20, 2026
b62af9b
test(reference-host): require observed retry before recovery
seonghobae Aug 21, 2026
a7f2c5a
fix(reference-host): require retry before recovery state
seonghobae Aug 21, 2026
20e0be5
test(reference-host): redact reconnect provider failures
seonghobae Aug 21, 2026
783dc9e
fix(reference-host): redact reconnect provider failures
seonghobae Aug 21, 2026
577512a
test(reference-host): redact host collaboration callback failures
seonghobae Aug 21, 2026
64212ae
fix(reference-host): redact collaboration callback failures
seonghobae Aug 21, 2026
c9b4a21
test(reference-host): bound prototype traversal
seonghobae Aug 21, 2026
be208da
fix(reference-host): bound prototype discovery
seonghobae Aug 21, 2026
ac130d0
test(reference-host): redact proposal apply failures
seonghobae Aug 21, 2026
1e45d14
fix(reference-host): redact proposal apply failures
seonghobae Aug 21, 2026
8f1cebd
test(reference-host): model ambiguous committed writes
seonghobae Aug 21, 2026
1d7b07b
test(reference-host): distinguish ambiguous commit
seonghobae Aug 21, 2026
b7715e1
fix(reference-host): model ambiguous committed writes
seonghobae Aug 21, 2026
5143fe5
test(reference-host): bind ambiguity guidance
seonghobae Aug 21, 2026
4b211fe
docs(reference-host): clarify ambiguous recovery
seonghobae Aug 21, 2026
cc64cc1
test(reference-host): define native form journey contract
seonghobae Aug 21, 2026
cfae154
feat(reference-host): add native form buyer journey
seonghobae Aug 21, 2026
19b95ed
docs(reference-host): record native form buyer journey
seonghobae Aug 21, 2026
f81dfff
test(reference-host): resolve fixture path from repository root
seonghobae Aug 21, 2026
4a5d593
test(reference-host): require single-flight authorized submit
seonghobae Aug 21, 2026
a4ddda4
fix(reference-host): serialize authorized submissions
seonghobae Aug 21, 2026
ae5ce15
fix(reference-host): guard overlapping native submits
seonghobae Aug 21, 2026
7c09378
test(reference-host): bind native form to single-flight submit
seonghobae Aug 21, 2026
de3f2a4
test(reference-host): require hydration-gated editor mount
seonghobae Aug 21, 2026
c53f9c6
feat(reference-host): add hydration-gated editor mount
seonghobae Aug 21, 2026
3e99bbb
test(reference-host): require packed artifact acceptance
seonghobae Aug 21, 2026
1b87cc0
feat(reference-host): verify exact packed artifact consumer
seonghobae Aug 21, 2026
297d2c3
test(reference-host): correct packed consumer install contract
seonghobae Aug 21, 2026
1c92709
fix(reference-host): validate consumer dependency resolution truthfully
seonghobae Aug 21, 2026
afe0236
test(reference-host): contain submission observer failures
seonghobae Aug 21, 2026
bfa9158
fix(reference-host): isolate submission observers
seonghobae Aug 21, 2026
95d60b4
test(reference-host): keep reset status honest
seonghobae Aug 21, 2026
8e6c941
fix(reference-host): keep reset save state consistent
seonghobae Aug 21, 2026
721a6c0
test(reference-host): keep native form contract source-isolated
seonghobae Aug 21, 2026
f0401c6
fix(reference-host): keep submission callback non-null
seonghobae Aug 21, 2026
3e65eda
test(reference-host): require read-only form fail-closed contract
seonghobae Aug 21, 2026
cf0c060
feat(reference-host): fail-close native form when read-only
seonghobae Aug 21, 2026
fca5b90
test(reference-host): align form assertions with read-only guard
seonghobae Aug 21, 2026
529b549
test(reference-host): require packed asset containment evidence
seonghobae Aug 21, 2026
2bc48a2
fix(reference-host): verify packed public asset containment
seonghobae Aug 21, 2026
9654d26
test(reference-host): require explicit client hydration boundary
seonghobae Aug 21, 2026
3eb10fb
fix(reference-host): mark hydration gate as client boundary
seonghobae Aug 21, 2026
9bc7ef9
test(reference-host): verify packed ESM and CommonJS consumers
seonghobae Aug 21, 2026
fc0add2
fix(reference-host): preserve packed verifier receipt contract
seonghobae Aug 21, 2026
2801626
test(reference-host): expose TypeScript import-guard gap
seonghobae Aug 21, 2026
6db306d
test(reference-host): guard all executable example imports
seonghobae Aug 21, 2026
c85a746
test(reference-host): expose synchronous submission gate
seonghobae Aug 21, 2026
77cd38d
test(reference-host): require synchronous form admission gate
seonghobae Aug 21, 2026
f8afbb7
fix(reference-host): expose synchronous submission admission
seonghobae Aug 21, 2026
f7bb0ea
fix(reference-host): bind form mutations to synchronous gate
seonghobae Aug 21, 2026
25fda4a
test(reference-host): reject unknown proposal fields
seonghobae Aug 21, 2026
199b80f
fix(reference-host): fail closed on unknown proposal fields
seonghobae Aug 21, 2026
47289f3
test(reference-host): reject unknown persistence fields
seonghobae Aug 21, 2026
2d1a591
fix(reference-host): fail closed on unknown persistence fields
seonghobae Aug 21, 2026
25ec179
test(reference-host): reject unknown collaboration option fields
seonghobae Aug 21, 2026
5c8aca0
fix(reference-host): reject unknown collaboration option fields
seonghobae Aug 21, 2026
df64f92
test(reference-host): reject unknown autosave snapshot fields
seonghobae Aug 21, 2026
ba723f6
fix(reference-host): require exact autosave snapshot shape
seonghobae Aug 21, 2026
88feb70
test(reference-host): expose incomplete collaboration teardown
seonghobae Aug 21, 2026
2b56d0d
fix(reference-host): retry incomplete collaboration teardown
seonghobae Aug 21, 2026
27d2e21
test(reference-host): lock partial teardown to cleanup
seonghobae Aug 21, 2026
a03f8f9
fix(reference-host): lock partial collaboration teardown
seonghobae Aug 21, 2026
aa174c0
test(reference-host): reject fork validator aliasing
seonghobae Aug 22, 2026
7f564f5
fix(reference-host): namespace fork validators
seonghobae Aug 22, 2026
dc6ea30
test(reference-host): lock fork validator namespace
seonghobae Aug 22, 2026
d235b8c
test(reference-host): bound nested fork validators
seonghobae Aug 22, 2026
1e3dabb
fix(reference-host): bound fork validator namespaces
seonghobae Aug 22, 2026
5c96976
test(reference-host): require packed markdown handoff
seonghobae Aug 22, 2026
48e27fe
feat(reference-host): verify packed markdown handoff
seonghobae Aug 22, 2026
f970ae1
test(reference-host): lock ambiguous connect quarantine
seonghobae Aug 22, 2026
f23f1a8
fix(reference-host): quarantine ambiguous provider connections
seonghobae Aug 22, 2026
ec7f78d
test(reference-host): align connect failure recovery contract
seonghobae Aug 22, 2026
271ade4
docs(reference-host): reconcile executable slice inventory
seonghobae Aug 22, 2026
1b5d346
test(reference-host): keep buyer inventory code-current
seonghobae Aug 22, 2026
c625004
test(reference-host): require packed autosave observer wiring
seonghobae Aug 22, 2026
94e64dc
feat(reference-host): wire packed autosave observer
seonghobae Aug 22, 2026
8b14d1e
test(reference-host): require autosave wiring inventory truth
seonghobae Aug 22, 2026
f12e0da
docs(reference-host): record packed autosave observer wiring
seonghobae Aug 22, 2026
96b871e
test(reference-host): require bounded Office handoff
seonghobae Aug 22, 2026
226cf92
feat(reference-host): add bounded Office handoff
seonghobae Aug 22, 2026
f14ebb4
docs(reference-host): document Office handoff boundary
seonghobae Aug 22, 2026
dc5e9d8
docs(reference-host): preserve browser acceptance contract
seonghobae Aug 22, 2026
c855622
test(reference-host): require application-level hydration
seonghobae Aug 22, 2026
fde3b0c
feat(reference-host): compose application hydration boundary
seonghobae Aug 22, 2026
45b2117
test(reference-host): keep hydration proof at package boundary
seonghobae Aug 22, 2026
029b6eb
docs(reference-host): inventory application composition
seonghobae Aug 22, 2026
7d03815
test(reference-host): require real browser hydration journey
seonghobae Aug 22, 2026
fe9cbba
feat(reference-host): add deterministic browser shell
seonghobae Aug 22, 2026
ee9bc97
feat(reference-host): hydrate browser host application
seonghobae Aug 22, 2026
b32fbe8
docs(reference-host): inventory browser host evidence
seonghobae Aug 22, 2026
a700a21
fix(reference-host): preserve hydration shell text nodes
seonghobae Aug 22, 2026
9a0cc37
test(reference-host): cover read-only browser journey
seonghobae Aug 22, 2026
bc0b357
docs(reference-host): record read-only browser evidence
seonghobae Aug 22, 2026
4198b4c
test(reference-host): cover narrow viewport journey
seonghobae Aug 22, 2026
a7c120b
docs(reference-host): record narrow viewport evidence
seonghobae Aug 22, 2026
a05dd94
test(reference-host): exercise buyer print journey
seonghobae Aug 22, 2026
b851c18
fix(reference-host): load public editor presentation contract
seonghobae Aug 22, 2026
95dcc93
docs(reference-host): align buyer print evidence
seonghobae Aug 22, 2026
a589f65
test(reference-host): exercise forced-colors buyer journey
seonghobae Aug 22, 2026
6649758
test(reference-host): require packed browser package binding
seonghobae Aug 22, 2026
5ceae9a
fix(reference-host): bind release browser to packed package
seonghobae Aug 22, 2026
93a06e1
test(reference-host): bound autosave validators
seonghobae Aug 22, 2026
7ea44dc
fix(reference-host): bound autosave validators
seonghobae Aug 22, 2026
afd6f57
test(reference-host): reject malformed strong entity tags
seonghobae Aug 22, 2026
890be94
fix(reference-host): validate strong entity-tag syntax
seonghobae Aug 22, 2026
08eb81d
test(reference-host): require controlled and uncontrolled form modes
seonghobae Aug 22, 2026
93512cd
feat(reference-host): demonstrate controlled editor composition
seonghobae Aug 22, 2026
1b43592
test(reference-host): match conditional controlled binding
seonghobae Aug 22, 2026
89cc471
test(ci): require packed reference-host browser artifact
seonghobae Aug 22, 2026
8aa2370
fix(ci): bind reference-host browser evidence to packed artifact
seonghobae Aug 22, 2026
49eae9a
fix(ci): align packed browser identity contract
seonghobae Aug 22, 2026
369b963
test(reference-host): contain Office handoff reflection
seonghobae Aug 22, 2026
1bc2075
test(reference-host): isolate Office reflection harness
seonghobae Aug 22, 2026
f82d09a
fix(reference-host): contain Office handoff reflection
seonghobae Aug 22, 2026
04f307f
test(reference-host): expose async proposal apply false-success
seonghobae Aug 22, 2026
b94845b
fix(reference-host): settle async proposal callbacks
seonghobae Aug 22, 2026
4472094
test(reference-host): expose dropped control mode
seonghobae Aug 22, 2026
f530b71
fix(reference-host): preserve app control mode
seonghobae Aug 22, 2026
d07ca94
test(reference-host): reject async provider connect false-success
seonghobae Aug 22, 2026
3976752
fix(reference-host): quarantine async provider connect
seonghobae Aug 22, 2026
8228971
test(reference-host): reject async provider teardown false-success
seonghobae Aug 22, 2026
31970f3
test(reference-host): cover async document teardown false-success
seonghobae Aug 22, 2026
931f6f9
test(reference-host): cover async provider disconnect false-success
seonghobae Aug 22, 2026
93dcc0b
fix(reference-host): contain async provider teardown
seonghobae Aug 22, 2026
05ab985
test(reference-host): reproduce stale saved state after edits
seonghobae Aug 23, 2026
39fcd98
test(reference-host): exercise saved-state invalidation in browser
seonghobae Aug 23, 2026
f53a68b
test(reference-host): move dirty-state RED to packed browser
seonghobae Aug 23, 2026
f2156ce
fix(ci): run every declared browser acceptance spec
seonghobae Aug 23, 2026
c0e97bb
test(reference-host): allow deterministic deferred save journey
seonghobae Aug 23, 2026
03beed6
test(reference-host): reproduce edit during in-flight save
seonghobae Aug 23, 2026
3b89870
test(reference-host): type browser network guard explicitly
seonghobae Aug 23, 2026
5580c35
fix(reference-host): invalidate stale saved presentation
seonghobae Aug 23, 2026
65cb6cf
test(reference-host): contract truthful dirty-state projection
seonghobae Aug 23, 2026
85532ca
fix(reference-host): invalidate stale save state on native input
seonghobae Aug 23, 2026
c2f137b
test(reference-host): contract native dirty-state fallback
seonghobae Aug 23, 2026
db68844
test(reference-host): make save-state assertions exact
seonghobae Aug 23, 2026
61cdec2
test(reference-host): require browser presentation entrypoint
seonghobae Aug 23, 2026
74e2160
test(browser): bind packed font entrypoints
seonghobae Aug 23, 2026
2a16930
feat(reference-host): exercise full packed presentation
seonghobae Aug 23, 2026
6a145db
docs(reference-host): align packed browser evidence
seonghobae Aug 23, 2026
e0a6c38
test(reference-host): align packed presentation binding
seonghobae Aug 23, 2026
eca9a3a
test(reference-host): align partial-app acceptance contract
seonghobae Aug 23, 2026
f2f3683
test(reference-host): verify pre-hydration shell transition
seonghobae Aug 23, 2026
1776469
test(reference-host): exercise packed native form reset
seonghobae Aug 23, 2026
33aafef
test(browser): pin optimizer entry contract
seonghobae Aug 23, 2026
942250e
fix(browser): stabilize shared Vite dependency graph
seonghobae Aug 23, 2026
ba6da1a
test(reference-host): require executable Office handoff acceptance
seonghobae Aug 23, 2026
3084ee9
feat(reference-host): execute packed Office handoff
seonghobae Aug 23, 2026
4b69e58
ci(reference-host): verify exact packed Office execution
seonghobae Aug 23, 2026
2722f86
test(reference-host): pin Office handoff export name
seonghobae Aug 23, 2026
bcc51c0
fix(ci): quote Office pip cache command
seonghobae Aug 23, 2026
f13b726
fix(reference-host): invoke canonical Office handoff export
seonghobae Aug 23, 2026
58699fc
test(reference-host): keep Office execution docs truthful
seonghobae Aug 23, 2026
cbfe14a
docs(reference-host): align Office execution evidence
seonghobae Aug 23, 2026
a34dc33
test(reference-host): require controlled browser composition
seonghobae Aug 23, 2026
dda8bc5
fix(reference-host): route controlled browser mode
seonghobae Aug 23, 2026
02475eb
fix(reference-host): expose active form control mode
seonghobae Aug 23, 2026
9ac26a0
test(reference-host): preserve form contract with mode marker
seonghobae Aug 23, 2026
9f6bc03
test(reference-host): enter heading content without markdown marker
seonghobae Aug 23, 2026
2a950c5
test(reference-host): assert rendered heading text on reset
seonghobae Aug 23, 2026
898bc4a
test(reference-host): prove read-only form boundary
seonghobae Aug 23, 2026
e86fd96
test(reference-host): require host authorization before collaboration…
seonghobae Aug 23, 2026
5aa0d7e
feat(reference-host): gate collaboration provider with host authoriza…
seonghobae Aug 23, 2026
f44b327
docs(reference-host): synchronize authorized collaboration inventory
seonghobae Aug 23, 2026
1b6b505
test(reference-host): require one-command buyer journey
seonghobae Aug 23, 2026
9ea1bd1
feat(reference-host): add current journey verifier
seonghobae Aug 23, 2026
0f89e06
docs(reference-host): expose current journey command
seonghobae Aug 23, 2026
71c3f84
test(reference-host): enforce runtime env independence
seonghobae Aug 23, 2026
d8884dc
test(reference-host): scope env contract to runtime files
seonghobae Aug 23, 2026
3423d82
test(reference-host): cover same-turn reset admission
seonghobae Aug 23, 2026
c33c420
test(reference-host): require browser journey in one-command plan
seonghobae Aug 23, 2026
b6c05b2
feat(reference-host): verify packed browser journey
seonghobae Aug 23, 2026
dbbb2a9
feat(reference-host): include browser acceptance in journey
seonghobae Aug 23, 2026
308669b
docs(reference-host): document scoped browser verifier
seonghobae Aug 23, 2026
52ecab7
test(reference-host): verify packed browser plan contract
seonghobae Aug 23, 2026
ff72919
test(reference-host): require self-contained Office journey
seonghobae Aug 24, 2026
7ca2f8f
feat(reference-host): self-contain packed Office journey
seonghobae Aug 24, 2026
8f34d64
fix(reference-host): execute self-contained packed Office step
seonghobae Aug 24, 2026
10438f6
docs(reference-host): distinguish packed Office orchestration
seonghobae Aug 24, 2026
559800e
test(reference-host): bind Office docs to packed authority split
seonghobae Aug 24, 2026
d6bbf03
ci(reference-host): execute self-contained packed Office journey
seonghobae Aug 24, 2026
4249c5e
test(reference-host): require narrow client hydration boundary
seonghobae Aug 24, 2026
a8012c5
feat(reference-host): isolate client hydration boundary
seonghobae Aug 24, 2026
743ce42
fix(reference-host): keep server shell outside client boundary
seonghobae Aug 24, 2026
64c3714
docs(reference-host): describe server and client boundary
seonghobae Aug 24, 2026
f2368c6
chore(reference-host): restore canonical CI ownership
seonghobae Aug 24, 2026
325d07c
test(reference-host): keep Office contract source-owned
seonghobae Aug 24, 2026
e86c27a
test(reference-host): drop foreign CI assertion
seonghobae Aug 24, 2026
031ac14
test(reference-host): require exact-packed app SSR proof
seonghobae Aug 24, 2026
20fdb0b
feat(reference-host): verify exact-packed app SSR
seonghobae Aug 24, 2026
23529fe
fix(reference-host): resolve SSR through packed consumer
seonghobae Aug 24, 2026
cfc18fe
docs(reference-host): bind exact-packed app SSR evidence
seonghobae Aug 24, 2026
a41000b
test(reference-host): require app SSR in one-command journey
seonghobae Aug 24, 2026
e02b7bc
fix(reference-host): include app SSR in current journey
seonghobae Aug 24, 2026
2805bdc
test(reference-host): require authorized collaboration in buyer journey
seonghobae Aug 25, 2026
ffba554
feat(reference-host): verify host collaboration authorization
seonghobae Aug 25, 2026
a76849b
fix(reference-host): run authorized collaboration journey
seonghobae Aug 25, 2026
3426b1f
test(reference-host): preserve Office paragraph boundaries
seonghobae Aug 25, 2026
33ff62d
fix(reference-host): preserve Office paragraph boundaries
seonghobae Aug 25, 2026
99a02f1
test(reference-host): verify multi-paragraph Office handoff
seonghobae Aug 25, 2026
46e0349
test(reference-host): prove packed framework-free buyer boundary
seonghobae Aug 25, 2026
94d2e81
test(reference-host): bound packed package setup time
seonghobae Aug 25, 2026
9536306
test(reference-host): reproduce same-turn reset admission gap
seonghobae Aug 25, 2026
da627ef
fix(reference-host): expose synchronous form mutation admission
seonghobae Aug 25, 2026
37cfc42
fix(reference-host): bind form handlers to synchronous admission
seonghobae Aug 25, 2026
1fb2d31
test(reference-host): assert shared synchronous form admission
seonghobae Aug 25, 2026
33c49bf
fix(markdown): keep headless package free of window authority
seonghobae Aug 28, 2026
f6e1c60
Revert "fix(markdown): keep headless package free of window authority"
seonghobae Aug 28, 2026
31e34ba
Merge remote-tracking branch 'origin/fix/public-markdown-resource-opt…
seonghobae Sep 4, 2026
df595a5
test(reference-host): select editor text portably
seonghobae Sep 4, 2026
4abae67
test(reference-host): preserve heading serialization
seonghobae Sep 4, 2026
d3113bb
chore(stack): synchronize Markdown resource parent
seonghobae Sep 4, 2026
0e6d2d1
feat(reference-host): connect autosave recovery to the editor
seonghobae Sep 5, 2026
ed4b020
test(reference-host): verify recovery races and document the demo
seonghobae Sep 5, 2026
92abb5d
test(reference-host): typecheck the executable browser host
seonghobae Sep 5, 2026
0214a8e
fix(reference-host): retain unsaved capture failures after older saves
seonghobae Sep 5, 2026
9ad3def
fix(reference-host): restore saved content before enabling autosave
seonghobae Sep 5, 2026
9b579e1
fix(reference-host): avoid rewriting normalized saved documents
seonghobae Sep 5, 2026
c05d630
fix(reference-host): install the packed browser dependency closure
seonghobae Sep 5, 2026
2fc0376
fix(reference-host): allow only the installed package assets
seonghobae Sep 5, 2026
a5f9e28
test(reference-host): verify keyboard replacement and complete browse…
seonghobae Sep 5, 2026
a5c7e51
docs(reference-host): clarify installed browser evidence and claim li…
seonghobae Sep 5, 2026
c92f886
fix(reference-host): keep package tests responsive during child builds
seonghobae Sep 5, 2026
28c6da9
test(reference-host): reject ambient Python authority in Office handoff
seonghobae Sep 5, 2026
885c5bc
fix(reference-host): use the project Office environment by default
seonghobae Sep 5, 2026
ebca39e
test: require confirmed saved-version recovery
seonghobae Sep 5, 2026
ca0242b
feat: confirm saved-version recovery in the reference host
seonghobae Sep 5, 2026
177d396
test: cover saved-version recovery races and rejected content
seonghobae Sep 5, 2026
eb56eaf
test: target the editable draft in restore race checks
seonghobae Sep 5, 2026
f6f8e08
fix: observe pending browser edits before confirmed restore
seonghobae Sep 5, 2026
9242f3e
fix: retain restore rejection feedback after pending captures
seonghobae Sep 5, 2026
013990b
test: require an interactive stale-proposal journey
seonghobae Sep 5, 2026
9a2f802
feat: review delayed proposals through public revision guards
seonghobae Sep 5, 2026
bb8ba1b
test: exercise delayed proposal application and read-only recovery
seonghobae Sep 5, 2026
03433fb
test: bind delayed preparation to its captured draft
seonghobae Sep 5, 2026
f6b4305
test: require interactive local collaboration lifecycle
seonghobae Sep 5, 2026
bee5cfb
feat: compose an authorized local collaboration demo
seonghobae Sep 5, 2026
3a5e2b4
test: exercise collaboration recovery admission and teardown
seonghobae Sep 5, 2026
dd3fc68
fix: return collaboration focus after controls become available
seonghobae Sep 5, 2026
5b80ed0
test: reject an unconfirmed disconnection claim
seonghobae Sep 5, 2026
8a38ceb
fix: keep ambiguous collaboration status unconfirmed
seonghobae Sep 5, 2026
f16e3f4
Merge commit 'd6341cf9e96020a4129289514e583ad063a154e1' into referenc…
seonghobae Sep 5, 2026
03dde1d
Merge commit '7ed887a2701a8c4916437179e06d590d1d413115' into referenc…
seonghobae Sep 5, 2026
94afa42
merge: inherit canonical encoder owner reconciliation
seonghobae Sep 5, 2026
9ecda1c
fix(reference-host): keep native control styles outside the editor
seonghobae Sep 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions examples/reference-host/README.md

Large diffs are not rendered by default.

322 changes: 322 additions & 0 deletions examples/reference-host/autosave-recovery-host.tsx

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions examples/reference-host/autosave-recovery.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.reference-recovery {
max-inline-size: 52rem;
margin-inline: auto;
padding: 1rem;
color: CanvasText;
background: Canvas;
font-family: 'Noto Sans', system-ui, sans-serif;
line-height: 1.5;
overflow-wrap: anywhere;
}

.reference-recovery output {
display: block;
margin-block: 1rem;
}

.reference-recovery-actions,
.reference-recovery-controls {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-block: 1rem;
}

.reference-recovery-controls { min-inline-size: 0; }
.reference-recovery-controls label { min-inline-size: 0; }
.reference-recovery-controls select { display: block; }
.reference-recovery-actions > button,
.reference-recovery-controls button,
.reference-recovery-controls select {
max-inline-size: 100%;
min-block-size: 2.75rem;
padding: 0.5rem;
font: inherit;
}
.reference-recovery-actions :focus-visible,
.reference-recovery-controls :focus-visible { outline: 2px solid Highlight; outline-offset: 2px; }

@media print {
.reference-recovery-actions,
.reference-recovery-controls { display: none; }
.reference-recovery { max-inline-size: none; padding: 0; }
}
311 changes: 311 additions & 0 deletions examples/reference-host/autosave-view-model.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
const AUTOSAVE_STATES = new Set([
'idle',
'saving',
'blocked',
'closing',
'closed',
]);
const BLOCKED_REASONS = new Set(['conflict', 'failure']);
const MAX_STRONG_ENTITY_TAG_CODE_UNITS = 256;
const STRONG_ENTITY_TAG_PATTERN = /^"[\x21\x23-\x7e\x80-\xff]*"$/u;
const SNAPSHOT_KEYS = [
'state',
'blockedReason',
'activeStrongEntityTag',
'pendingStrongEntityTag',
'lastSavedStrongEntityTag',
];

/** Marker used by repository contracts to prevent this host fixture becoming runtime authority. */
export const REFERENCE_ONLY = true;

function requireNullableStrongEntityTag(value, label) {
if (
value !== null &&
(typeof value !== 'string' ||
value.length > MAX_STRONG_ENTITY_TAG_CODE_UNITS ||
!STRONG_ENTITY_TAG_PATTERN.test(value))
) {
throw new TypeError(`${label} is invalid.`);
}
return value;
}

function snapshotData(source) {
try {
if (typeof source !== 'object' || source === null) {
throw new TypeError('autosave snapshot is invalid.');
}
const values = Object.create(null);
for (const key of SNAPSHOT_KEYS) {
const descriptor = Object.getOwnPropertyDescriptor(source, key);
if (
descriptor === undefined ||
!Object.prototype.hasOwnProperty.call(descriptor, 'value')
) {
throw new TypeError('autosave snapshot is invalid.');
}
values[key] = descriptor.value;
}
const ownKeys = Reflect.ownKeys(source);
if (
ownKeys.length !== SNAPSHOT_KEYS.length ||
ownKeys.some(
(key) => typeof key !== 'string' || !SNAPSHOT_KEYS.includes(key),
)
) {
throw new TypeError('autosave snapshot is invalid.');
}
return values;
} catch {
throw new TypeError('autosave snapshot is invalid.');
}
}

function readSnapshot(source) {
const snapshot = snapshotData(source);
if (
!AUTOSAVE_STATES.has(snapshot.state) ||
(snapshot.blockedReason !== null &&
!BLOCKED_REASONS.has(snapshot.blockedReason))
) {
throw new TypeError('autosave snapshot is invalid.');
}

if (
(snapshot.state === 'blocked' && snapshot.blockedReason === null) ||
(snapshot.state !== 'blocked' && snapshot.blockedReason !== null)
) {
throw new TypeError('autosave snapshot lifecycle is inconsistent.');
}

return Object.freeze({
state: snapshot.state,
blockedReason: snapshot.blockedReason,
activeStrongEntityTag: requireNullableStrongEntityTag(
snapshot.activeStrongEntityTag,
'activeStrongEntityTag',
),
pendingStrongEntityTag: requireNullableStrongEntityTag(
snapshot.pendingStrongEntityTag,
'pendingStrongEntityTag',
),
lastSavedStrongEntityTag: requireNullableStrongEntityTag(
snapshot.lastSavedStrongEntityTag,
'lastSavedStrongEntityTag',
),
});
}

function presentation(viewState) {
return Object.freeze({
viewState,
messageKey: `referenceHost.autosave.${viewState}`,
busy:
viewState === 'saving' ||
viewState === 'queued' ||
viewState === 'retrying',
blocked: viewState === 'conflict' || viewState === 'failed',
canRetry: viewState === 'conflict' || viewState === 'failed',
});
}

/**
* Create one host-owned projection of Inkspan autosave lifecycle transitions.
*
* `observe()` consumes only programmatic queue/session snapshots. Snapshot fields
* must be an exact own-data-property shape so presentation never invokes
* caller-owned accessors or silently admits authority-looking metadata. Strong
* entity tags are validated as bounded RFC 9110 strong entity-tags before
* projection so untrusted snapshot metadata cannot masquerade as a durable
* validator or allocate unbounded retained strings at this reference boundary.
* A blocked to saving transition is presented as retrying, and only a later idle
* transition after that observed retry is presented as recovered. A blocked to
* idle transition without an intervening save returns to clean instead of
* manufacturing recovery evidence. The projection never returns local or durable
* validators; hosts localize `messageKey` and keep authenticated recovery controls
* outside Inkspan.
*/
export function createAutosaveViewModel() {
let retryPending = false;
let retryInFlight = false;

function clearRetryEvidence() {
retryPending = false;
retryInFlight = false;
}

function observe(snapshot) {
const current = readSnapshot(snapshot);

if (current.state === 'blocked') {
retryPending = true;
retryInFlight = false;
return presentation(
current.blockedReason === 'conflict' ? 'conflict' : 'failed',
);
}
if (current.state === 'closing') {
clearRetryEvidence();
return presentation('closing');
}
if (current.state === 'closed') {
clearRetryEvidence();
return presentation('closed');
}
if (current.state === 'saving' && (retryPending || retryInFlight)) {
retryPending = false;
retryInFlight = true;
return presentation('retrying');
}
if (current.state === 'saving' && current.pendingStrongEntityTag !== null) {
return presentation('queued');
}
if (current.state === 'saving') return presentation('saving');
if (retryInFlight) {
clearRetryEvidence();
return presentation('recovered');
}
retryPending = false;
return presentation('clean');
}

return Object.freeze({ observe });
}

function snapshot({
state,
blockedReason = null,
activeStrongEntityTag = null,
pendingStrongEntityTag = null,
lastSavedStrongEntityTag = null,
}) {
return Object.freeze({
state,
blockedReason,
activeStrongEntityTag,
pendingStrongEntityTag,
lastSavedStrongEntityTag,
});
}

function runSelfTest() {
const steady = createAutosaveViewModel();
const clean = steady.observe(snapshot({ state: 'idle' })).viewState;
const saving = steady.observe(
snapshot({
state: 'saving',
activeStrongEntityTag: '"local-active"',
}),
).viewState;
const queued = steady.observe(
snapshot({
state: 'saving',
activeStrongEntityTag: '"local-active"',
pendingStrongEntityTag: '"local-pending"',
}),
).viewState;

const recovery = createAutosaveViewModel();
const conflict = recovery.observe(
snapshot({ state: 'blocked', blockedReason: 'conflict' }),
).viewState;
const retrying = recovery.observe(
snapshot({
state: 'saving',
activeStrongEntityTag: '"local-retry"',
}),
).viewState;
const recovered = recovery.observe(
snapshot({
state: 'idle',
lastSavedStrongEntityTag: '"local-saved"',
}),
).viewState;

const failed = createAutosaveViewModel().observe(
snapshot({ state: 'blocked', blockedReason: 'failure' }),
).viewState;
const closing = createAutosaveViewModel().observe(
snapshot({ state: 'closing' }),
).viewState;
const closed = createAutosaveViewModel().observe(
snapshot({ state: 'closed' }),
).viewState;

process.stdout.write(
`${JSON.stringify({
clean,
closed,
closing,
conflict,
failed,
queued,
recovered,
retrying,
saving,
})}\n`,
);
}

function runInvalidValidatorSelfTest() {
function observeError(candidate) {
try {
createAutosaveViewModel().observe(candidate);
return null;
} catch (error) {
return error instanceof Error ? error.message : 'unexpected error';
}
}

const activeError = observeError(
snapshot({ state: 'saving', activeStrongEntityTag: '' }),
);
const pendingError = observeError(
snapshot({
state: 'saving',
activeStrongEntityTag: '"local-active"',
pendingStrongEntityTag: '',
}),
);
const lastSavedError = observeError(
snapshot({ state: 'idle', lastSavedStrongEntityTag: '' }),
);

process.stdout.write(
`${JSON.stringify({ activeError, lastSavedError, pendingError })}\n`,
);
}

function runHostileAccessorSelfTest() {
let getterCalls = 0;
let error = null;
const hostile = {
blockedReason: null,
activeStrongEntityTag: null,
pendingStrongEntityTag: null,
lastSavedStrongEntityTag: null,
};
Object.defineProperty(hostile, 'state', {
enumerable: true,
get() {
getterCalls += 1;
return 'idle';
},
});
try {
createAutosaveViewModel().observe(hostile);
} catch (failure) {
error = failure instanceof Error ? failure.message : 'unexpected error';
}
process.stdout.write(`${JSON.stringify({ error, getterCalls })}\n`);
}

const commandArguments = typeof process === 'undefined' ? [] : process.argv;
if (commandArguments.includes('--invalid-validator-self-test')) {
runInvalidValidatorSelfTest();
} else if (commandArguments.includes('--hostile-accessor-self-test')) {
runHostileAccessorSelfTest();
} else if (commandArguments.includes('--self-test')) {
runSelfTest();
}
12 changes: 12 additions & 0 deletions examples/reference-host/browser-host.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Inkspan reference host</title>
</head>
<body>
<div id="reference-host-root"><main aria-labelledby="reference-host-heading"><h1 id="reference-host-heading">Inkspan reference host</h1><div aria-busy="true">Loading buyer editor</div></main></div>
<script type="module" src="./browser-host.tsx"></script>
</body>
</html>
Loading