cursedbelt-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/dist/client/wsWire.d.ts +49 -0
- package/dist/client/wsWire.js +57 -0
- package/dist/config/appConfig.d.ts +361 -0
- package/dist/config/appConfig.js +308 -0
- package/dist/core/activity/index.d.ts +8 -0
- package/dist/core/activity/index.js +7 -0
- package/dist/core/activity/model.d.ts +115 -0
- package/dist/core/activity/model.js +63 -0
- package/dist/core/analytics/index.d.ts +18 -0
- package/dist/core/analytics/index.js +18 -0
- package/dist/core/analytics/range.d.ts +186 -0
- package/dist/core/analytics/range.js +271 -0
- package/dist/core/analytics/schedule.d.ts +301 -0
- package/dist/core/analytics/schedule.js +372 -0
- package/dist/core/category/index.d.ts +3 -0
- package/dist/core/category/index.js +6 -0
- package/dist/core/category/memoryStore.d.ts +21 -0
- package/dist/core/category/memoryStore.js +97 -0
- package/dist/core/category/tree.d.ts +26 -0
- package/dist/core/category/tree.js +113 -0
- package/dist/core/category/types.d.ts +48 -0
- package/dist/core/category/types.js +8 -0
- package/dist/core/color.d.ts +14 -0
- package/dist/core/color.js +32 -0
- package/dist/core/colorScheme/colorSchemeStore.d.ts +70 -0
- package/dist/core/colorScheme/colorSchemeStore.js +123 -0
- package/dist/core/colorScheme/index.d.ts +7 -0
- package/dist/core/colorScheme/index.js +7 -0
- package/dist/core/domainOwners.d.ts +19 -0
- package/dist/core/domainOwners.js +15 -0
- package/dist/core/download.d.ts +14 -0
- package/dist/core/download.js +37 -0
- package/dist/core/events/ccEventBus.d.ts +44 -0
- package/dist/core/events/ccEventBus.js +70 -0
- package/dist/core/file-tree/fileTreeModel.d.ts +276 -0
- package/dist/core/file-tree/fileTreeModel.js +312 -0
- package/dist/core/folder-tree/actions.d.ts +84 -0
- package/dist/core/folder-tree/actions.js +119 -0
- package/dist/core/folder-tree/index.d.ts +3 -0
- package/dist/core/folder-tree/index.js +20 -0
- package/dist/core/folder-tree/path.d.ts +76 -0
- package/dist/core/folder-tree/path.js +146 -0
- package/dist/core/folder-tree/tree.d.ts +86 -0
- package/dist/core/folder-tree/tree.js +184 -0
- package/dist/core/layout/index.d.ts +1 -0
- package/dist/core/layout/index.js +7 -0
- package/dist/core/master-lock/index.d.ts +14 -0
- package/dist/core/master-lock/index.js +14 -0
- package/dist/core/master-lock/kdf.d.ts +63 -0
- package/dist/core/master-lock/kdf.js +117 -0
- package/dist/core/master-lock/policy.d.ts +32 -0
- package/dist/core/master-lock/policy.js +52 -0
- package/dist/core/master-lock/presence.d.ts +52 -0
- package/dist/core/master-lock/presence.js +61 -0
- package/dist/core/master-lock/wire.d.ts +139 -0
- package/dist/core/master-lock/wire.js +81 -0
- package/dist/core/media/bgRemoval.d.ts +27 -0
- package/dist/core/media/bgRemoval.js +101 -0
- package/dist/core/media/ffmpeg.d.ts +57 -0
- package/dist/core/media/ffmpeg.js +126 -0
- package/dist/core/media/format.d.ts +20 -0
- package/dist/core/media/format.js +54 -0
- package/dist/core/media/index.d.ts +12 -0
- package/dist/core/media/index.js +18 -0
- package/dist/core/media/mediaItem.d.ts +15 -0
- package/dist/core/media/mediaItem.js +34 -0
- package/dist/core/media/renderPlan.d.ts +63 -0
- package/dist/core/media/renderPlan.js +232 -0
- package/dist/core/media/segment.d.ts +79 -0
- package/dist/core/media/segment.js +247 -0
- package/dist/core/media/size.d.ts +1 -0
- package/dist/core/media/size.js +3 -0
- package/dist/core/media/trim.d.ts +21 -0
- package/dist/core/media/trim.js +40 -0
- package/dist/core/media/types.d.ts +148 -0
- package/dist/core/media/types.js +11 -0
- package/dist/core/media/uploadLimits.d.ts +7 -0
- package/dist/core/media/uploadLimits.js +16 -0
- package/dist/core/media/validate.d.ts +9 -0
- package/dist/core/media/validate.js +121 -0
- package/dist/core/media/videoCuts.d.ts +152 -0
- package/dist/core/media/videoCuts.js +284 -0
- package/dist/core/navigation/breadcrumbCompat.d.ts +24 -0
- package/dist/core/navigation/breadcrumbCompat.js +21 -0
- package/dist/core/navigation/coverageTest.d.ts +24 -0
- package/dist/core/navigation/coverageTest.js +48 -0
- package/dist/core/navigation/index.d.ts +6 -0
- package/dist/core/navigation/index.js +10 -0
- package/dist/core/navigation/migrateNavPreferences.d.ts +55 -0
- package/dist/core/navigation/migrateNavPreferences.js +63 -0
- package/dist/core/navigation/navigationModel.d.ts +165 -0
- package/dist/core/navigation/navigationModel.js +14 -0
- package/dist/core/navigation/navigationSelectors.d.ts +63 -0
- package/dist/core/navigation/navigationSelectors.js +167 -0
- package/dist/core/navigation/searchCatalogue.d.ts +23 -0
- package/dist/core/navigation/searchCatalogue.js +76 -0
- package/dist/core/net/reconnectSchedule.d.ts +46 -0
- package/dist/core/net/reconnectSchedule.js +38 -0
- package/dist/core/palette/ShortcutRegistry.d.ts +33 -0
- package/dist/core/palette/ShortcutRegistry.js +52 -0
- package/dist/core/palette/fuzzyRank.d.ts +18 -0
- package/dist/core/palette/fuzzyRank.js +73 -0
- package/dist/core/palette/hotkeys.d.ts +28 -0
- package/dist/core/palette/hotkeys.js +118 -0
- package/dist/core/palette/index.d.ts +3 -0
- package/dist/core/palette/index.js +8 -0
- package/dist/core/schema/projection.d.ts +84 -0
- package/dist/core/schema/projection.js +184 -0
- package/dist/core/session/claims.d.ts +145 -0
- package/dist/core/session/claims.js +157 -0
- package/dist/core/sharing/index.d.ts +5 -0
- package/dist/core/sharing/index.js +4 -0
- package/dist/core/sharing/model.d.ts +195 -0
- package/dist/core/sharing/model.js +150 -0
- package/dist/core/site/siteCopy.d.ts +92 -0
- package/dist/core/site/siteCopy.js +91 -0
- package/dist/core/slots.d.ts +22 -0
- package/dist/core/slots.js +31 -0
- package/dist/core/styles/constructableSheet.d.ts +51 -0
- package/dist/core/styles/constructableSheet.js +80 -0
- package/dist/core/tokens/registry.d.ts +119 -0
- package/dist/core/tokens/registry.js +447 -0
- package/dist/core/uiScale/index.d.ts +8 -0
- package/dist/core/uiScale/index.js +8 -0
- package/dist/core/uiScale/scale.d.ts +36 -0
- package/dist/core/uiScale/scale.js +47 -0
- package/dist/core/uiScale/uiScaleStore.d.ts +47 -0
- package/dist/core/uiScale/uiScaleStore.js +63 -0
- package/dist/core/wire/__fixtures__/rowsMarshalWorker.d.ts +1 -0
- package/dist/core/wire/__fixtures__/rowsMarshalWorker.js +23 -0
- package/dist/core/wire/index.d.ts +17 -0
- package/dist/core/wire/index.js +17 -0
- package/dist/core/wire/ingress.d.ts +44 -0
- package/dist/core/wire/ingress.js +134 -0
- package/dist/core/wire/marshalWorker.d.ts +70 -0
- package/dist/core/wire/marshalWorker.js +273 -0
- package/dist/core/wire/marshalling.d.ts +122 -0
- package/dist/core/wire/marshalling.js +126 -0
- package/dist/core/wire/wireHub.d.ts +80 -0
- package/dist/core/wire/wireHub.js +116 -0
- package/dist/ctgr/base64url.d.ts +8 -0
- package/dist/ctgr/base64url.js +39 -0
- package/dist/ctgr/chunkAccumulator.d.ts +214 -0
- package/dist/ctgr/chunkAccumulator.js +307 -0
- package/dist/ctgr/chunkStore.d.ts +24 -0
- package/dist/ctgr/chunkStore.js +125 -0
- package/dist/ctgr/codec.d.ts +44 -0
- package/dist/ctgr/codec.js +153 -0
- package/dist/ctgr/compress.d.ts +2 -0
- package/dist/ctgr/compress.js +20 -0
- package/dist/ctgr/crc32.d.ts +2 -0
- package/dist/ctgr/crc32.js +23 -0
- package/dist/ctgr/digest.d.ts +1 -0
- package/dist/ctgr/digest.js +15 -0
- package/dist/ctgr/index.d.ts +9 -0
- package/dist/ctgr/index.js +17 -0
- package/dist/ctgr/types.d.ts +64 -0
- package/dist/ctgr/types.js +21 -0
- package/dist/ctgr/v0compat.d.ts +27 -0
- package/dist/ctgr/v0compat.js +128 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +17 -0
- package/dist/shared/errors.d.ts +101 -0
- package/dist/shared/errors.js +99 -0
- package/dist/testing/bytes.d.ts +32 -0
- package/dist/testing/bytes.js +33 -0
- package/dist/testing/domInspect.d.ts +59 -0
- package/dist/testing/domInspect.js +115 -0
- package/dist/testing/fakeFetch.d.ts +23 -0
- package/dist/testing/fakeFetch.js +21 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.js +11 -0
- package/package.json +373 -0
- package/src/client/wsWire.ts +83 -0
- package/src/config/appConfig.spec.ts +158 -0
- package/src/config/appConfig.ts +372 -0
- package/src/core/activity/index.ts +21 -0
- package/src/core/activity/model.ts +160 -0
- package/src/core/analytics/index.ts +18 -0
- package/src/core/analytics/range.spec.ts +280 -0
- package/src/core/analytics/range.ts +383 -0
- package/src/core/analytics/schedule.spec.ts +311 -0
- package/src/core/analytics/schedule.ts +624 -0
- package/src/core/category/category.spec.ts +144 -0
- package/src/core/category/index.ts +19 -0
- package/src/core/category/memoryStore.ts +104 -0
- package/src/core/category/tree.ts +118 -0
- package/src/core/category/types.ts +59 -0
- package/src/core/color.spec.ts +35 -0
- package/src/core/color.ts +35 -0
- package/src/core/colorScheme/colorScheme.spec.ts +127 -0
- package/src/core/colorScheme/colorSchemeStore.ts +184 -0
- package/src/core/colorScheme/index.ts +23 -0
- package/src/core/domainOwners.ts +32 -0
- package/src/core/download.spec.ts +38 -0
- package/src/core/download.ts +35 -0
- package/src/core/events/ccEventBus.spec.ts +90 -0
- package/src/core/events/ccEventBus.ts +120 -0
- package/src/core/file-tree/fileTreeModel.spec.ts +462 -0
- package/src/core/file-tree/fileTreeModel.ts +511 -0
- package/src/core/folder-tree/actions.spec.ts +103 -0
- package/src/core/folder-tree/actions.ts +153 -0
- package/src/core/folder-tree/index.ts +46 -0
- package/src/core/folder-tree/path.spec.ts +113 -0
- package/src/core/folder-tree/path.ts +148 -0
- package/src/core/folder-tree/tree.spec.ts +157 -0
- package/src/core/folder-tree/tree.ts +244 -0
- package/src/core/layout/index.ts +7 -0
- package/src/core/master-lock/index.ts +50 -0
- package/src/core/master-lock/kdf.spec.ts +95 -0
- package/src/core/master-lock/kdf.ts +145 -0
- package/src/core/master-lock/policy.ts +55 -0
- package/src/core/master-lock/presence.spec.ts +73 -0
- package/src/core/master-lock/presence.ts +71 -0
- package/src/core/master-lock/wire.ts +155 -0
- package/src/core/media/bgRemoval.spec.ts +120 -0
- package/src/core/media/bgRemoval.ts +123 -0
- package/src/core/media/ffmpeg.spec.ts +135 -0
- package/src/core/media/ffmpeg.ts +162 -0
- package/src/core/media/format.spec.ts +41 -0
- package/src/core/media/format.ts +58 -0
- package/src/core/media/index.ts +105 -0
- package/src/core/media/mediaItem.spec.ts +46 -0
- package/src/core/media/mediaItem.ts +40 -0
- package/src/core/media/renderPlan.spec.ts +140 -0
- package/src/core/media/renderPlan.ts +318 -0
- package/src/core/media/segment.spec.ts +159 -0
- package/src/core/media/segment.ts +352 -0
- package/src/core/media/size.spec.ts +19 -0
- package/src/core/media/size.ts +3 -0
- package/src/core/media/trim.spec.ts +75 -0
- package/src/core/media/trim.ts +60 -0
- package/src/core/media/types.ts +147 -0
- package/src/core/media/uploadLimits.ts +18 -0
- package/src/core/media/validate.spec.ts +106 -0
- package/src/core/media/validate.ts +131 -0
- package/src/core/media/videoCuts.spec.ts +327 -0
- package/src/core/media/videoCuts.ts +355 -0
- package/src/core/navigation/breadcrumbCompat.ts +44 -0
- package/src/core/navigation/coverageTest.ts +54 -0
- package/src/core/navigation/index.ts +50 -0
- package/src/core/navigation/migrateNavPreferences.ts +119 -0
- package/src/core/navigation/navigation.spec.ts +388 -0
- package/src/core/navigation/navigationModel.ts +185 -0
- package/src/core/navigation/navigationSelectors.ts +206 -0
- package/src/core/navigation/searchCatalogue.ts +89 -0
- package/src/core/net/reconnectSchedule.spec.ts +49 -0
- package/src/core/net/reconnectSchedule.ts +78 -0
- package/src/core/palette/ShortcutRegistry.ts +69 -0
- package/src/core/palette/fuzzyRank.ts +78 -0
- package/src/core/palette/hotkeys.ts +132 -0
- package/src/core/palette/index.ts +22 -0
- package/src/core/palette/palette.spec.ts +160 -0
- package/src/core/schema/projection.spec.ts +122 -0
- package/src/core/schema/projection.ts +225 -0
- package/src/core/session/claims.ts +181 -0
- package/src/core/sharing/index.ts +33 -0
- package/src/core/sharing/model.spec.ts +104 -0
- package/src/core/sharing/model.ts +296 -0
- package/src/core/site/siteCopy.ts +115 -0
- package/src/core/slots.ts +38 -0
- package/src/core/styles/constructableSheet.ts +86 -0
- package/src/core/tokens/registry.spec.ts +160 -0
- package/src/core/tokens/registry.ts +536 -0
- package/src/core/uiScale/index.ts +32 -0
- package/src/core/uiScale/scale.ts +63 -0
- package/src/core/uiScale/uiScale.spec.ts +77 -0
- package/src/core/uiScale/uiScaleStore.ts +100 -0
- package/src/core/wire/__fixtures__/rowsMarshalWorker.ts +24 -0
- package/src/core/wire/index.ts +56 -0
- package/src/core/wire/ingress.spec.ts +276 -0
- package/src/core/wire/ingress.ts +182 -0
- package/src/core/wire/marshalWorker.spec.ts +196 -0
- package/src/core/wire/marshalWorker.ts +361 -0
- package/src/core/wire/marshalling.spec.ts +139 -0
- package/src/core/wire/marshalling.ts +238 -0
- package/src/core/wire/wireHub.spec.ts +137 -0
- package/src/core/wire/wireHub.ts +213 -0
- package/src/ctgr/base64url.ts +44 -0
- package/src/ctgr/chunkAccumulator.ts +403 -0
- package/src/ctgr/chunkStore.spec.ts +101 -0
- package/src/ctgr/chunkStore.ts +168 -0
- package/src/ctgr/codec.spec.ts +208 -0
- package/src/ctgr/codec.ts +202 -0
- package/src/ctgr/compress.ts +21 -0
- package/src/ctgr/crc32.ts +23 -0
- package/src/ctgr/digest.ts +14 -0
- package/src/ctgr/index.ts +39 -0
- package/src/ctgr/types.ts +80 -0
- package/src/ctgr/v0compat.spec.ts +73 -0
- package/src/ctgr/v0compat.ts +156 -0
- package/src/declaredDepsAreImported.spec.ts +87 -0
- package/src/index.ts +25 -0
- package/src/leafSubpathsImportNothing.spec.ts +287 -0
- package/src/lockfileIsTracked.spec.ts +50 -0
- package/src/namedSubpathsResolve.spec.ts +122 -0
- package/src/noPathDeps.spec.ts +24 -0
- package/src/publishShape.spec.ts +89 -0
- package/src/shared/errors.ts +153 -0
- package/src/skippedTestsAreAnswered.spec.ts +221 -0
- package/src/testTmpRoot.spec.ts +102 -0
- package/src/testing/bytes.ts +38 -0
- package/src/testing/domInspect.spec.ts +79 -0
- package/src/testing/domInspect.ts +127 -0
- package/src/testing/fakeFetch.ts +26 -0
- package/src/testing/index.ts +11 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The marketing-site copy contract — owner decision **D36** (2026-07-27).
|
|
3
|
+
*
|
|
4
|
+
* ## What D36 actually asked for
|
|
5
|
+
*
|
|
6
|
+
* The owner's answer to "give me the words" was: *"I want to see it first, use
|
|
7
|
+
* your judgment for now. Make an admin section where I can edit them. Put in
|
|
8
|
+
* placeholders or good values now. Values I edit are in the db. I potentially
|
|
9
|
+
* switch to hardcoding them if this is too slow for the page loads to get and
|
|
10
|
+
* fill in the text, caching may help."*
|
|
11
|
+
*
|
|
12
|
+
* That decomposes into one contract with three consumers, and this module is
|
|
13
|
+
* the single source all three import so they can never disagree about what a
|
|
14
|
+
* key is or what its default says:
|
|
15
|
+
*
|
|
16
|
+
* 1. **The static site build** (`site/build.ts`) BAKES every default into the
|
|
17
|
+
* emitted HTML. Crawlers, Lighthouse and a JS-disabled visitor see a
|
|
18
|
+
* complete page; SEO never depends on a fetch.
|
|
19
|
+
* 2. **The server** (`guard.mixin.ts` + `drivers/siteCopy.ts`) stores the
|
|
20
|
+
* owner's EDITS — only the overridden keys — and serves them from a
|
|
21
|
+
* cacheable public endpoint.
|
|
22
|
+
* 3. **The admin panel** lists every key with its default beside the stored
|
|
23
|
+
* value, so "reset" is a visible diff rather than a guess.
|
|
24
|
+
*
|
|
25
|
+
* The owner's "switch to hardcoding" exit ramp is already built: the bake step
|
|
26
|
+
* IS the hardcoding; deleting the page's overlay fetch is the whole switch.
|
|
27
|
+
*
|
|
28
|
+
* ## Why a closed key list rather than free-form KV
|
|
29
|
+
*
|
|
30
|
+
* Every key here has a home in the page template. A free-form store would let
|
|
31
|
+
* an edit succeed against a key nothing renders — a silent no-op the owner
|
|
32
|
+
* would read as "editing is broken". `setSiteCopyValue` refuses unknown keys,
|
|
33
|
+
* so a typo is a 400 naming the valid keys, not a row nobody reads.
|
|
34
|
+
*
|
|
35
|
+
* `seo.*` keys are baked-only by nature: the runtime overlay swaps visible
|
|
36
|
+
* text nodes, and a crawler never runs it — so an edited `seo.title` takes
|
|
37
|
+
* effect at the NEXT site build. The admin panel says so per key
|
|
38
|
+
* ({@link SITE_COPY_BAKED_ONLY}).
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/** Ceiling on one stored value. The longest default is ~400 chars; 4000 leaves
|
|
42
|
+
* room for a generous rewrite while refusing a pasted novel (the value rides
|
|
43
|
+
* an HTTP header-cached JSON body every visitor may fetch). */
|
|
44
|
+
export const SITE_COPY_MAX_VALUE_LENGTH = 4000;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Every editable string on the marketing page, with the shipped default.
|
|
48
|
+
* Ordered as the page reads, because this order is also the admin panel's.
|
|
49
|
+
*/
|
|
50
|
+
export const SITE_COPY_DEFAULTS = {
|
|
51
|
+
'product.name': 'Cursed Alchemy',
|
|
52
|
+
'product.tagline': 'Build real, multi-user web applications visually. Identity, data, and media included.',
|
|
53
|
+
|
|
54
|
+
'seo.title': 'Cursed Alchemy — Build visual architectures. Deploy production apps.',
|
|
55
|
+
'seo.description':
|
|
56
|
+
'A visual studio engineered for resilient web applications. Combine core modules — user auth, document databases, media storage — on a live canvas that validates your architecture and deploys production-ready apps.',
|
|
57
|
+
|
|
58
|
+
'hero.title': 'Build visual architectures. Deploy production apps.',
|
|
59
|
+
'hero.subtitle':
|
|
60
|
+
'A visual studio engineered for building resilient web applications. Combine core modules—like user auth, databases, and media storage—on a live canvas that validates and deploys your stack automatically.',
|
|
61
|
+
'hero.cta': 'Coming soon — sign-ups open shortly.',
|
|
62
|
+
|
|
63
|
+
'feature.studio.kicker': 'Infrastructure Engine',
|
|
64
|
+
'feature.studio.title': 'Orchestrated for Production',
|
|
65
|
+
'feature.studio.body':
|
|
66
|
+
'Drag core infrastructure onto a live canvas and connect it seamlessly. The studio analyzes structural dependencies in real time, catching broken configurations instantly — before they ever reach production.',
|
|
67
|
+
'feature.engines.kicker': 'Unified Ecosystem',
|
|
68
|
+
'feature.engines.title': 'Sovereign Infrastructure, Built In',
|
|
69
|
+
'feature.engines.body':
|
|
70
|
+
'Native authentication, relational databases, media vaults, and productivity tooling ship as one unified ecosystem — maintained centrally, running at peak performance, with no brittle third-party plugins to babysit.',
|
|
71
|
+
'feature.ownership.kicker': 'Data Governance',
|
|
72
|
+
'feature.ownership.title': 'Complete Ownership of Your Architecture',
|
|
73
|
+
'feature.ownership.body':
|
|
74
|
+
'Every workspace runs in strict multi-tenant isolation with comprehensive change logs, automated backups, and total data portability. Your environment belongs to you — zero vendor lock-in. Ever.',
|
|
75
|
+
|
|
76
|
+
'modules.kicker': 'Batteries Included',
|
|
77
|
+
'modules.title': 'Every core module. One canvas.',
|
|
78
|
+
'modules.body':
|
|
79
|
+
'User accounts, document databases, media vaults, scheduling, email, and staging sandboxes — first-party engines that snap together and ship as one verified stack.',
|
|
80
|
+
|
|
81
|
+
'plans.heading': 'Plans',
|
|
82
|
+
'plans.note':
|
|
83
|
+
'Pricing is being finalized. Join today as an early tester to secure free access and help shape the product roadmap.',
|
|
84
|
+
'plan.free.name': 'Explorer',
|
|
85
|
+
'plan.free.price': 'Free',
|
|
86
|
+
'plan.free.body': '• Build and host one workspace\n• Access to foundational modules',
|
|
87
|
+
'plan.pro.name': 'Alchemist',
|
|
88
|
+
'plan.pro.price': 'Coming soon',
|
|
89
|
+
'plan.pro.body': '• Unlock all advanced modules\n• Expanded storage limits\n• Priority developer support',
|
|
90
|
+
'plan.team.name': 'Guild',
|
|
91
|
+
'plan.team.price': 'Coming soon',
|
|
92
|
+
'plan.team.body':
|
|
93
|
+
'• Collaborative team workspaces\n• Role-based access control (RBAC)\n• Full system audit logs',
|
|
94
|
+
|
|
95
|
+
'about.heading': 'About',
|
|
96
|
+
'about.body':
|
|
97
|
+
'Cursed Alchemy is built independently with an uncompromising focus on core infrastructure—authentication, isolation, secure backups, and audit trails. We master the complex foundational architecture so you can focus entirely on your unique user experience.',
|
|
98
|
+
|
|
99
|
+
'signin.label': 'Sign in',
|
|
100
|
+
'footer.note': '© Cursed Alchemy. All rights reserved.',
|
|
101
|
+
} as const;
|
|
102
|
+
|
|
103
|
+
export type SiteCopyKey = keyof typeof SITE_COPY_DEFAULTS;
|
|
104
|
+
|
|
105
|
+
export const SITE_COPY_KEYS = Object.freeze(Object.keys(SITE_COPY_DEFAULTS)) as readonly SiteCopyKey[];
|
|
106
|
+
|
|
107
|
+
export const isSiteCopyKey = (value: string): value is SiteCopyKey =>
|
|
108
|
+
Object.hasOwn(SITE_COPY_DEFAULTS, value);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Keys the runtime overlay cannot apply (they live in `<head>`, which only a
|
|
112
|
+
* rebuild re-emits). The admin panel labels these "applies at next site
|
|
113
|
+
* publish" instead of pretending the edit is live.
|
|
114
|
+
*/
|
|
115
|
+
export const SITE_COPY_BAKED_ONLY: readonly SiteCopyKey[] = Object.freeze(['seo.title', 'seo.description']);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Slot registry (02 §6.1) — a closed set. A fill targeting anything else is
|
|
3
|
+
* V1 `slot-unknown`, which is the difference between "my panel silently never
|
|
4
|
+
* rendered" and a build error naming the typo.
|
|
5
|
+
*/
|
|
6
|
+
export const SLOTS = [
|
|
7
|
+
'HeaderLeft',
|
|
8
|
+
'HeaderRight', // app chrome (auth UI renders here by default)
|
|
9
|
+
'SidebarTop',
|
|
10
|
+
'SidebarFooter',
|
|
11
|
+
'InspectorTab', // fills add tabs: {tabTitle, panel}
|
|
12
|
+
'DashboardWidget', // SystemUtility dashboard grid cells
|
|
13
|
+
'ItemContextMenu',
|
|
14
|
+
'EditorToolbar',
|
|
15
|
+
'StatusBar',
|
|
16
|
+
] as const;
|
|
17
|
+
|
|
18
|
+
export type SlotId = (typeof SLOTS)[number];
|
|
19
|
+
|
|
20
|
+
const SLOT_SET: ReadonlySet<string> = new Set<string>(SLOTS);
|
|
21
|
+
|
|
22
|
+
export const isSlotId = (value: string): value is SlotId => SLOT_SET.has(value);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Fixed z-index bands (02 §6.2). Literal z-index values anywhere else are a lint
|
|
26
|
+
* error; the Portal Provider assigns `bandBase + stackDepth` inside the band so
|
|
27
|
+
* two engines opening dialogs at once stack deterministically.
|
|
28
|
+
*/
|
|
29
|
+
export const Z_BANDS = {
|
|
30
|
+
content: 0,
|
|
31
|
+
sticky: 100,
|
|
32
|
+
overlay: 1000,
|
|
33
|
+
popover: 1100,
|
|
34
|
+
toast: 1200,
|
|
35
|
+
critical: 1300, // GuardEngine lock screen — always wins
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
export type ZBand = keyof typeof Z_BANDS;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Constructable Stylesheet adopter (02 §5.2, ruling J2-4).
|
|
3
|
+
*
|
|
4
|
+
* Engines carrying third-party global stylesheets declare
|
|
5
|
+
* `cssIsolation: 'shadow'` and mount inside a ShadowRoot. The naive way to give
|
|
6
|
+
* those roots the framework's tokens is to inline a `<style>` copy of the sheet
|
|
7
|
+
* into each one — which duplicates the entire Tailwind/token CSSOM per instance.
|
|
8
|
+
* A dashboard with eight shadow panels then pays eight full parses and eight
|
|
9
|
+
* allocations, and every theme change re-parses all eight.
|
|
10
|
+
*
|
|
11
|
+
* Instead the shell compiles the sheet **exactly once at boot** into an
|
|
12
|
+
* immutable `CSSStyleSheet` and every shadow boundary adopts it **by
|
|
13
|
+
* reference**. The browser shares one CSSOM allocation across N boundaries, so a
|
|
14
|
+
* multi-panel dashboard costs one sheet, not N.
|
|
15
|
+
*
|
|
16
|
+
* A shadow root that inlines its own copy is V5 ERROR `stylesheet-duplication`.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
let sharedSheet: CSSStyleSheet | null = null;
|
|
20
|
+
|
|
21
|
+
/** Does this environment support Constructable Stylesheets? */
|
|
22
|
+
export const supportsConstructableStylesheets = (): boolean =>
|
|
23
|
+
typeof CSSStyleSheet !== 'undefined' &&
|
|
24
|
+
typeof (CSSStyleSheet.prototype as { replaceSync?: unknown }).replaceSync === 'function' &&
|
|
25
|
+
typeof ShadowRoot !== 'undefined' &&
|
|
26
|
+
'adoptedStyleSheets' in ShadowRoot.prototype;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Compile the shared sheet. Call ONCE at boot with the framework's
|
|
30
|
+
* token/Tailwind CSS text; later calls are ignored so a second caller cannot
|
|
31
|
+
* silently replace the sheet every boundary is already pointing at.
|
|
32
|
+
*/
|
|
33
|
+
export function initSharedStyleSheet(cssText: string): CSSStyleSheet | null {
|
|
34
|
+
if (sharedSheet !== null) return sharedSheet;
|
|
35
|
+
if (!supportsConstructableStylesheets()) return null;
|
|
36
|
+
const sheet = new CSSStyleSheet();
|
|
37
|
+
sheet.replaceSync(cssText);
|
|
38
|
+
sharedSheet = sheet;
|
|
39
|
+
return sheet;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const getSharedStyleSheet = (): CSSStyleSheet | null => sharedSheet;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Adopt the shared sheet (plus an optional instance-local one) into a shadow
|
|
46
|
+
* root — **by reference, never by copy**.
|
|
47
|
+
*
|
|
48
|
+
* Returns false when the platform lacks the API; the caller then falls back to a
|
|
49
|
+
* single `<link>` in the shadow root, which still avoids inlining the text N
|
|
50
|
+
* times.
|
|
51
|
+
*/
|
|
52
|
+
export function adoptSharedStyles(root: ShadowRoot, instanceLocalSheet?: CSSStyleSheet): boolean {
|
|
53
|
+
if (sharedSheet === null || !supportsConstructableStylesheets()) return false;
|
|
54
|
+
const sheets = instanceLocalSheet ? [sharedSheet, instanceLocalSheet] : [sharedSheet];
|
|
55
|
+
// Assigning the shared object itself is the entire point — a `.slice()` of its
|
|
56
|
+
// rules into a fresh sheet would reintroduce the per-instance allocation.
|
|
57
|
+
root.adoptedStyleSheets = sheets;
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Build an instance-local sheet for an engine's own scoped CSS. Kept separate
|
|
63
|
+
* from the shared one so a per-instance override never mutates the object every
|
|
64
|
+
* other boundary adopted.
|
|
65
|
+
*/
|
|
66
|
+
export function createInstanceStyleSheet(cssText: string): CSSStyleSheet | null {
|
|
67
|
+
if (!supportsConstructableStylesheets()) return null;
|
|
68
|
+
const sheet = new CSSStyleSheet();
|
|
69
|
+
sheet.replaceSync(cssText);
|
|
70
|
+
return sheet;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Test seam — drops the shared sheet so a suite can re-init cleanly. */
|
|
74
|
+
export const resetSharedStyleSheet = (): void => {
|
|
75
|
+
sharedSheet = null;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The `@scope` wrapper for `cssIsolation: 'scope'`.
|
|
80
|
+
*
|
|
81
|
+
* Lighter than a shadow root: the subtree keeps inheriting the app's tokens
|
|
82
|
+
* (a shadow boundary would cut them off), while a reset stops the third-party
|
|
83
|
+
* sheet's global selectors from leaking outward.
|
|
84
|
+
*/
|
|
85
|
+
export const scopedCss = (scopeSelector: string, cssText: string): string =>
|
|
86
|
+
`@scope (${scopeSelector}) { :scope { all: revert-layer; } ${cssText} }`;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import {
|
|
4
|
+
BASE_TOKENS,
|
|
5
|
+
compareSchemaShapes,
|
|
6
|
+
createTokenRegistry,
|
|
7
|
+
describeSchema,
|
|
8
|
+
isExtensionTokenId,
|
|
9
|
+
stagingPluginOf,
|
|
10
|
+
type TokenDef,
|
|
11
|
+
TokenRegistry,
|
|
12
|
+
} from './registry';
|
|
13
|
+
|
|
14
|
+
const token = (over: Partial<TokenDef> & { id: string }): TokenDef => ({
|
|
15
|
+
mode: 'value',
|
|
16
|
+
description: 'fixture token',
|
|
17
|
+
schema: z.object({ id: z.string() }),
|
|
18
|
+
sample: { id: 'x' },
|
|
19
|
+
...over,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('base registry', () => {
|
|
23
|
+
it('constructs with every base token and rejects nothing', () => {
|
|
24
|
+
const registry = createTokenRegistry();
|
|
25
|
+
expect(registry.ids().length).toBe(BASE_TOKENS.length);
|
|
26
|
+
for (const base of BASE_TOKENS) expect(registry.has(base.id)).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('every base token sample satisfies its own schema (the V7.4 wire smoke payload)', () => {
|
|
30
|
+
for (const base of BASE_TOKENS) expect(base.schema.safeParse(base.sample).success).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('refuses a base token in the reserved staging namespace', () => {
|
|
34
|
+
expect(() => new TokenRegistry([token({ id: 'x-habits:mark' })])).toThrow(/token-namespace-violation/);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('lattice membership (04 V3)', () => {
|
|
39
|
+
const registry = createTokenRegistry();
|
|
40
|
+
|
|
41
|
+
it('is reflexive and transitive over subtypeOf', () => {
|
|
42
|
+
expect(registry.isSubtype('media:videos', 'media:videos')).toBe(true);
|
|
43
|
+
expect(registry.isSubtype('media:videos', 'media:items')).toBe(true);
|
|
44
|
+
expect(registry.isSubtype('media:items', 'media:videos')).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('connects a narrower producer to a wider consumer — string equality is retired', () => {
|
|
48
|
+
expect(registry.canConnect('media:videos', 'media:items').ok).toBe(true);
|
|
49
|
+
expect(registry.canConnect('media:photos', 'media:items').ok).toBe(true);
|
|
50
|
+
const wrongWay = registry.canConnect('media:items', 'media:videos');
|
|
51
|
+
expect(wrongWay.ok).toBe(false);
|
|
52
|
+
expect(wrongWay.reason).toBe('not-a-subtype');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('refuses signal↔value mismatches', () => {
|
|
56
|
+
const mismatch = registry.canConnect('signal:refresh', 'text:content');
|
|
57
|
+
expect(mismatch.ok).toBe(false);
|
|
58
|
+
expect(mismatch.reason).toBe('mode-mismatch');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('names the unknown side when a token is not registered', () => {
|
|
62
|
+
expect(registry.canConnect('nope:thing', 'media:items').reason).toBe('unknown-produced');
|
|
63
|
+
expect(registry.canConnect('media:items', 'nope:thing').reason).toBe('unknown-consumed');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('staging extension laws', () => {
|
|
68
|
+
it('accepts a namespaced extension that narrows a core token', () => {
|
|
69
|
+
const registry = createTokenRegistry();
|
|
70
|
+
const issues = registry.registerAll(
|
|
71
|
+
[token({ id: 'x-habits:mark', mode: 'signal', subtypeOf: 'signal:refresh', schema: z.object({ habitId: z.string() }), sample: { habitId: 'h' } })],
|
|
72
|
+
{ origin: 'staging', path: '/staging/plugin-habits/tokens.token-extension.ts' },
|
|
73
|
+
);
|
|
74
|
+
expect(issues).toEqual([]);
|
|
75
|
+
expect(registry.canConnect('x-habits:mark', 'signal:refresh').ok).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('reports a redeclaration of a core token as a collision, not a namespace problem', () => {
|
|
79
|
+
const registry = createTokenRegistry();
|
|
80
|
+
const issues = registry.registerAll([token({ id: 'media:videos' })], { origin: 'staging' });
|
|
81
|
+
expect(issues.map((i) => i.code)).toEqual(['token-collision']);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('rejects an un-namespaced staging token', () => {
|
|
85
|
+
const registry = createTokenRegistry();
|
|
86
|
+
const issues = registry.registerAll([token({ id: 'habits:mark' })], { origin: 'staging' });
|
|
87
|
+
expect(issues.map((i) => i.code)).toEqual(['token-namespace-violation']);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('rejects a subtype inversion — the core may never depend on an extension', () => {
|
|
91
|
+
const registry = createTokenRegistry();
|
|
92
|
+
registry.registerAll([token({ id: 'x-habits:mark', mode: 'signal', subtypeOf: 'signal:refresh', schema: z.object({}), sample: {} })], {
|
|
93
|
+
origin: 'staging',
|
|
94
|
+
});
|
|
95
|
+
const issues = registry.registerAll([token({ id: 'x-other:thing', subtypeOf: 'x-habits:mark' })], { origin: 'staging' });
|
|
96
|
+
expect(issues).toEqual([]); // extension → extension is fine
|
|
97
|
+
|
|
98
|
+
const inverted = new TokenRegistry([]);
|
|
99
|
+
inverted.registerAll([token({ id: 'x-habits:mark', mode: 'signal', schema: z.object({}), sample: {} })], { origin: 'staging' });
|
|
100
|
+
const coreIssues = inverted.registerAll([token({ id: 'media:videos', subtypeOf: 'x-habits:mark' })], { origin: 'base' });
|
|
101
|
+
expect(coreIssues.map((i) => i.code)).toEqual(['subtype-inversion']);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('rejects an extension whose sample does not satisfy its own schema', () => {
|
|
105
|
+
const registry = createTokenRegistry();
|
|
106
|
+
const issues = registry.registerAll([token({ id: 'x-habits:bad', schema: z.object({ id: z.string() }), sample: { id: 7 } })], {
|
|
107
|
+
origin: 'staging',
|
|
108
|
+
});
|
|
109
|
+
expect(issues.map((i) => i.code)).toEqual(['token-sample-invalid']);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('reports a missing supertype rather than registering a dangling edge', () => {
|
|
113
|
+
const registry = createTokenRegistry();
|
|
114
|
+
const issues = registry.registerAll([token({ id: 'x-habits:mark', subtypeOf: 'ghost:token' })], { origin: 'staging' });
|
|
115
|
+
expect(issues.map((i) => i.code)).toEqual(['token-supertype-missing']);
|
|
116
|
+
expect(registry.has('x-habits:mark')).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('maps extension ids back to their plugin', () => {
|
|
120
|
+
expect(isExtensionTokenId('x-habits:mark')).toBe(true);
|
|
121
|
+
expect(isExtensionTokenId('media:videos')).toBe(false);
|
|
122
|
+
expect(stagingPluginOf('x-habits:mark')).toBe('plugin-habits');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('structural schema comparison (J4-D)', () => {
|
|
127
|
+
const shape = (schema: z.ZodType) => describeSchema(schema);
|
|
128
|
+
|
|
129
|
+
it('calls identical shapes identical', () => {
|
|
130
|
+
expect(compareSchemaShapes(shape(z.object({ id: z.string() })), shape(z.object({ id: z.string() })))).toBe('identical');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('accepts an added optional field as backward compatible', () => {
|
|
134
|
+
expect(
|
|
135
|
+
compareSchemaShapes(shape(z.object({ id: z.string(), name: z.string().optional() })), shape(z.object({ id: z.string() }))),
|
|
136
|
+
).toBe('backward-compatible-extension');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('rejects an added REQUIRED field', () => {
|
|
140
|
+
expect(compareSchemaShapes(shape(z.object({ id: z.string(), name: z.string() })), shape(z.object({ id: z.string() })))).toBe('divergent');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('rejects a producer that drops a field the consumer requires', () => {
|
|
144
|
+
expect(compareSchemaShapes(shape(z.object({ id: z.string() })), shape(z.object({ id: z.string(), name: z.string() })))).toBe('divergent');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('recurses through arrays — the skew is never at the top level', () => {
|
|
148
|
+
const consumer = shape(z.object({ items: z.array(z.object({ id: z.string() })) }));
|
|
149
|
+
const extended = shape(z.object({ items: z.array(z.object({ id: z.string(), caption: z.string().optional() })) }));
|
|
150
|
+
const drifted = shape(z.object({ items: z.array(z.object({ id: z.number() })) }));
|
|
151
|
+
expect(compareSchemaShapes(extended, consumer)).toBe('backward-compatible-extension');
|
|
152
|
+
expect(compareSchemaShapes(drifted, consumer)).toBe('divergent');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('treats a required→optional demotion of a consumer field as drift', () => {
|
|
156
|
+
const consumer = shape(z.object({ id: z.string() }));
|
|
157
|
+
const producer = shape(z.object({ id: z.string().optional() }));
|
|
158
|
+
expect(compareSchemaShapes(producer, consumer)).toBe('divergent');
|
|
159
|
+
});
|
|
160
|
+
});
|