@toa.io/extensions.configuration 1.0.0-alpha.272 → 1.0.0-alpha.274
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/CHANGELOG.md +210 -0
- package/components/configuration.values/manifest.toa.yaml +158 -0
- package/components/configuration.values/source/create.ts +56 -0
- package/components/configuration.values/source/fetch.ts +24 -0
- package/components/configuration.values/source/get.ts +33 -0
- package/components/configuration.values/source/lib/map.test.ts +34 -0
- package/components/configuration.values/source/lib/map.ts +34 -0
- package/components/configuration.values/source/lib/resolve.ts +54 -0
- package/components/configuration.values/source/list.ts +19 -0
- package/components/configuration.values/tsconfig.json +9 -0
- package/cucumber.js +8 -0
- package/features/site/_app/immutable/asset.js +1 -0
- package/features/site/index.html +6 -0
- package/features/steps/UI.ts +88 -0
- package/features/steps/tsconfig.json +9 -0
- package/features/ui.feature +85 -0
- package/package.json +14 -7
- package/readme.md +160 -47
- package/schemas/annotation.cos.yaml +29 -1
- package/schemas/manifest.cos.yaml +9 -3
- package/source/Aspect.test.ts +84 -8
- package/source/Aspect.ts +59 -5
- package/source/Client.test.ts +104 -0
- package/source/Client.ts +258 -0
- package/source/Composition.ts +57 -0
- package/source/Factory.ts +28 -5
- package/source/Secret.test.ts +29 -0
- package/source/Secret.ts +29 -0
- package/source/UI.ts +197 -0
- package/source/configuration.test.ts +92 -39
- package/source/configuration.ts +30 -11
- package/source/const.ts +17 -0
- package/source/deployment.test.ts +80 -1
- package/source/deployment.ts +73 -24
- package/source/epoch.test.ts +26 -0
- package/source/epoch.ts +21 -0
- package/source/index.ts +2 -1
- package/transpiled/Aspect.d.ts +18 -4
- package/transpiled/Aspect.js +47 -3
- package/transpiled/Aspect.js.map +1 -1
- package/transpiled/Aspect.test.js +59 -9
- package/transpiled/Aspect.test.js.map +1 -1
- package/transpiled/Client.d.ts +61 -0
- package/transpiled/Client.js +173 -0
- package/transpiled/Client.js.map +1 -0
- package/transpiled/Client.test.d.ts +1 -0
- package/transpiled/Client.test.js +77 -0
- package/transpiled/Client.test.js.map +1 -0
- package/transpiled/Composition.d.ts +14 -0
- package/transpiled/Composition.js +43 -0
- package/transpiled/Composition.js.map +1 -0
- package/transpiled/Factory.d.ts +8 -2
- package/transpiled/Factory.js +17 -2
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Secret.d.ts +13 -0
- package/transpiled/Secret.js +26 -0
- package/transpiled/Secret.js.map +1 -0
- package/transpiled/Secret.test.d.ts +1 -0
- package/transpiled/Secret.test.js +26 -0
- package/transpiled/Secret.test.js.map +1 -0
- package/transpiled/UI.d.ts +25 -0
- package/transpiled/UI.js +186 -0
- package/transpiled/UI.js.map +1 -0
- package/transpiled/configuration.d.ts +7 -2
- package/transpiled/configuration.js +31 -13
- package/transpiled/configuration.js.map +1 -1
- package/transpiled/configuration.test.js +84 -37
- package/transpiled/configuration.test.js.map +1 -1
- package/transpiled/const.d.ts +11 -0
- package/transpiled/const.js +14 -0
- package/transpiled/const.js.map +1 -0
- package/transpiled/deployment.d.ts +10 -4
- package/transpiled/deployment.js +57 -21
- package/transpiled/deployment.js.map +1 -1
- package/transpiled/deployment.test.js +60 -0
- package/transpiled/deployment.test.js.map +1 -1
- package/transpiled/epoch.d.ts +2 -0
- package/transpiled/epoch.js +21 -0
- package/transpiled/epoch.js.map +1 -0
- package/transpiled/epoch.test.d.ts +1 -0
- package/transpiled/epoch.test.js +22 -0
- package/transpiled/epoch.test.js.map +1 -0
- package/transpiled/index.d.ts +2 -1
- package/transpiled/index.js +4 -1
- package/transpiled/index.js.map +1 -1
- package/transpiled/tsconfig.tsbuildinfo +1 -1
- package/ui/.agents/skills/antcn/SKILL.md +91 -0
- package/ui/.agents/skills/antcn/references/analytics.md +78 -0
- package/ui/.agents/skills/antcn/references/bridge.md +41 -0
- package/ui/.agents/skills/antcn/references/clipboard.md +23 -0
- package/ui/.agents/skills/antcn/references/dismissable.md +75 -0
- package/ui/.agents/skills/antcn/references/dropdown.md +153 -0
- package/ui/.agents/skills/antcn/references/fragments.md +29 -0
- package/ui/.agents/skills/antcn/references/fullscreen.md +157 -0
- package/ui/.agents/skills/antcn/references/geo.md +27 -0
- package/ui/.agents/skills/antcn/references/globals.md +31 -0
- package/ui/.agents/skills/antcn/references/grammatical-form.md +69 -0
- package/ui/.agents/skills/antcn/references/hints.md +44 -0
- package/ui/.agents/skills/antcn/references/hold.md +34 -0
- package/ui/.agents/skills/antcn/references/iam.md +117 -0
- package/ui/.agents/skills/antcn/references/input-form.md +34 -0
- package/ui/.agents/skills/antcn/references/locale.md +66 -0
- package/ui/.agents/skills/antcn/references/net.md +33 -0
- package/ui/.agents/skills/antcn/references/panel.md +44 -0
- package/ui/.agents/skills/antcn/references/passkeys.md +27 -0
- package/ui/.agents/skills/antcn/references/payments.md +99 -0
- package/ui/.agents/skills/antcn/references/realtime.md +69 -0
- package/ui/.agents/skills/antcn/references/scrollable.md +39 -0
- package/ui/.agents/skills/antcn/references/selector.md +71 -0
- package/ui/.agents/skills/antcn/references/shake.md +36 -0
- package/ui/.agents/skills/antcn/references/share.md +28 -0
- package/ui/.agents/skills/antcn/references/shell.md +139 -0
- package/ui/.agents/skills/antcn/references/stack.md +53 -0
- package/ui/.agents/skills/antcn/references/system.md +149 -0
- package/ui/.agents/skills/antcn/references/taptap.md +28 -0
- package/ui/.agents/skills/antcn/references/tools.md +68 -0
- package/ui/.agents/skills/antcn/references/transmission.md +96 -0
- package/ui/.agents/skills/shadcn-svelte/SKILL.md +227 -0
- package/ui/.agents/skills/shadcn-svelte/agents/openai.yml +5 -0
- package/ui/.agents/skills/shadcn-svelte/assets/shadcn-svelte-small.png +0 -0
- package/ui/.agents/skills/shadcn-svelte/assets/shadcn-svelte.png +0 -0
- package/ui/.agents/skills/shadcn-svelte/cli.md +166 -0
- package/ui/.agents/skills/shadcn-svelte/customization.md +213 -0
- package/ui/.agents/skills/shadcn-svelte/evals/evals.json +47 -0
- package/ui/.agents/skills/shadcn-svelte/rules/composition.md +244 -0
- package/ui/.agents/skills/shadcn-svelte/rules/forms.md +234 -0
- package/ui/.agents/skills/shadcn-svelte/rules/icons.md +107 -0
- package/ui/.agents/skills/shadcn-svelte/rules/styling.md +195 -0
- package/ui/.agents/skills/svas/SKILL.md +169 -0
- package/ui/.agents/skills/svas/references/patterns.md +95 -0
- package/ui/.agents/skills/svintl/SKILL.md +176 -0
- package/ui/.claude/settings.json +15 -0
- package/ui/.codex/hooks.json +15 -0
- package/ui/.cursor/hooks.json +11 -0
- package/ui/.env.example +1 -0
- package/ui/.prettierrc +9 -0
- package/ui/.vscode/extensions.json +7 -0
- package/ui/.vscode/settings.json +27 -0
- package/ui/AGENTS.md +51 -0
- package/ui/architecture.md +390 -0
- package/ui/components.json +20 -0
- package/ui/eslint.config.js +152 -0
- package/ui/features/base.ts +2 -0
- package/ui/features/boot.feature +5 -0
- package/ui/features/steps/authentication.ts +34 -0
- package/ui/features/steps/navigaton.ts +37 -0
- package/ui/features/steps/typing.ts +24 -0
- package/ui/package-lock.json +8173 -0
- package/ui/package.json +76 -0
- package/ui/playwright.config.ts +13 -0
- package/ui/skills-lock.json +30 -0
- package/ui/src/@/configuration/index.ts +1 -0
- package/ui/src/@/configuration/svc/create.ts +29 -0
- package/ui/src/@/configuration/svc/index.ts +3 -0
- package/ui/src/@/configuration/svc/net/Configuration.ts +18 -0
- package/ui/src/@/configuration/svc/net/index.ts +2 -0
- package/ui/src/@/configuration/svc/net/interface.ts +35 -0
- package/ui/src/@/configuration/svc/store.ts +14 -0
- package/ui/src/@/configuration/ui/Configurations.svelte +85 -0
- package/ui/src/@/configuration/ui/Configurations.ts +5 -0
- package/ui/src/@/configuration/ui/Create.svelte +64 -0
- package/ui/src/@/configuration/ui/Create.ts +7 -0
- package/ui/src/@/configuration/ui/Form.svelte +75 -0
- package/ui/src/@/configuration/ui/Form.ts +79 -0
- package/ui/src/@/configuration/ui/Row.svelte +44 -0
- package/ui/src/@/configuration/ui/Row.ts +9 -0
- package/ui/src/@/configuration/ui/Value.svelte +33 -0
- package/ui/src/@/configuration/ui/Value.ts +6 -0
- package/ui/src/@/configuration/ui/index.ts +9 -0
- package/ui/src/@/configuration/ui/intl/built.js +38 -0
- package/ui/src/@/configuration/ui/intl/context.yaml +51 -0
- package/ui/src/@/configuration/ui/intl/en-US.yaml +21 -0
- package/ui/src/@/configuration/ui/intl/index.ts +9 -0
- package/ui/src/@/configuration/ui/intl/types.ts +31 -0
- package/ui/src/@/configuration/ui/read.test.ts +75 -0
- package/ui/src/@/configuration/ui/read.ts +102 -0
- package/ui/src/@/configuration/ui/ui.test.ts +69 -0
- package/ui/src/@/configuration/ui/ui.ts +115 -0
- package/ui/src/@/iam/index.ts +1 -0
- package/ui/src/@/iam/rc.ts +22 -0
- package/ui/src/@/iam/svc/authenticated.ts +17 -0
- package/ui/src/@/iam/svc/basic.ts +23 -0
- package/ui/src/@/iam/svc/index.ts +10 -0
- package/ui/src/@/iam/svc/logout.ts +5 -0
- package/ui/src/@/iam/svc/named.ts +16 -0
- package/ui/src/@/iam/svc/net/Echo.ts +9 -0
- package/ui/src/@/iam/svc/net/basic/index.ts +1 -0
- package/ui/src/@/iam/svc/net/basic/interface.ts +15 -0
- package/ui/src/@/iam/svc/net/federation/index.ts +1 -0
- package/ui/src/@/iam/svc/net/federation/interface.ts +12 -0
- package/ui/src/@/iam/svc/net/index.ts +6 -0
- package/ui/src/@/iam/svc/net/interface.ts +17 -0
- package/ui/src/@/iam/svc/net/otp/index.ts +1 -0
- package/ui/src/@/iam/svc/net/otp/interface.ts +14 -0
- package/ui/src/@/iam/svc/net/passkeys/index.ts +1 -0
- package/ui/src/@/iam/svc/net/passkeys/interface.ts +12 -0
- package/ui/src/@/iam/svc/oidc/apple.ts +47 -0
- package/ui/src/@/iam/svc/oidc/authenticate.ts +62 -0
- package/ui/src/@/iam/svc/oidc/google.ts +45 -0
- package/ui/src/@/iam/svc/oidc/hello.ts +82 -0
- package/ui/src/@/iam/svc/oidc/index.ts +3 -0
- package/ui/src/@/iam/svc/oidc/nonce.ts +5 -0
- package/ui/src/@/iam/svc/oidc/providers.ts +28 -0
- package/ui/src/@/iam/svc/oidc/standard.ts +28 -0
- package/ui/src/@/iam/svc/otp.ts +18 -0
- package/ui/src/@/iam/svc/passkeys/create.ts +20 -0
- package/ui/src/@/iam/svc/passkeys/index.ts +2 -0
- package/ui/src/@/iam/svc/passkeys/login.ts +21 -0
- package/ui/src/@/iam/svc/store.ts +36 -0
- package/ui/src/@/iam/svc/sync.ts +18 -0
- package/ui/src/@/iam/svc/transfer.ts +11 -0
- package/ui/src/@/iam/svc/update.ts +8 -0
- package/ui/src/@/iam/ui/AccountLike.ts +4 -0
- package/ui/src/@/iam/ui/Authenticated.svelte +37 -0
- package/ui/src/@/iam/ui/Authenticated.ts +10 -0
- package/ui/src/@/iam/ui/Authentication.svelte +50 -0
- package/ui/src/@/iam/ui/Authentication.ts +9 -0
- package/ui/src/@/iam/ui/Authorized.svelte +32 -0
- package/ui/src/@/iam/ui/Authorized.ts +65 -0
- package/ui/src/@/iam/ui/Card.svelte +14 -0
- package/ui/src/@/iam/ui/Card.ts +7 -0
- package/ui/src/@/iam/ui/Refresh.svelte +39 -0
- package/ui/src/@/iam/ui/credentials/Credentials.svelte +26 -0
- package/ui/src/@/iam/ui/credentials/Credentials.ts +5 -0
- package/ui/src/@/iam/ui/credentials/Provider.svelte +24 -0
- package/ui/src/@/iam/ui/credentials/Provider.ts +5 -0
- package/ui/src/@/iam/ui/credentials/index.ts +1 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Key.svelte +42 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Key.ts +29 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Manager.svelte +83 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Manager.ts +5 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Passkeys.svelte +39 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Summary.svelte +27 -0
- package/ui/src/@/iam/ui/credentials/passkeys/Summary.ts +5 -0
- package/ui/src/@/iam/ui/credentials/passkeys/index.ts +1 -0
- package/ui/src/@/iam/ui/credentials/providers.ts +15 -0
- package/ui/src/@/iam/ui/credentials/transfer/Qr.svelte +24 -0
- package/ui/src/@/iam/ui/credentials/transfer/Qr.ts +3 -0
- package/ui/src/@/iam/ui/credentials/transfer/Transfer.svelte +48 -0
- package/ui/src/@/iam/ui/credentials/transfer/Transfer.ts +5 -0
- package/ui/src/@/iam/ui/credentials/transfer/index.ts +1 -0
- package/ui/src/@/iam/ui/index.ts +3 -0
- package/ui/src/@/iam/ui/intl/built.js +79 -0
- package/ui/src/@/iam/ui/intl/context.yaml +91 -0
- package/ui/src/@/iam/ui/intl/en-US.yaml +64 -0
- package/ui/src/@/iam/ui/intl/index.ts +10 -0
- package/ui/src/@/iam/ui/intl/types.ts +70 -0
- package/ui/src/@/iam/ui/oidc/Authentication.svelte +16 -0
- package/ui/src/@/iam/ui/oidc/Authentication.ts +5 -0
- package/ui/src/@/iam/ui/oidc/Button.svelte +36 -0
- package/ui/src/@/iam/ui/oidc/Button.ts +11 -0
- package/ui/src/@/iam/ui/oidc/Refresh.svelte +16 -0
- package/ui/src/@/iam/ui/oidc/icons/Apple.svelte +19 -0
- package/ui/src/@/iam/ui/oidc/icons/Google.svelte +31 -0
- package/ui/src/@/iam/ui/oidc/icons/index.ts +11 -0
- package/ui/src/@/iam/ui/oidc/index.ts +2 -0
- package/ui/src/@/iam/ui/passkey/Authentication.svelte +64 -0
- package/ui/src/@/iam/ui/passkey/Authentication.ts +8 -0
- package/ui/src/@/iam/ui/passkey/Create.svelte +53 -0
- package/ui/src/@/iam/ui/passkey/Create.ts +7 -0
- package/ui/src/@/iam/ui/passkey/Login.svelte +29 -0
- package/ui/src/@/iam/ui/passkey/Refresh.svelte +29 -0
- package/ui/src/@/iam/ui/passkey/index.ts +2 -0
- package/ui/src/@/iam/ui/password/Authentication.svelte +19 -0
- package/ui/src/@/iam/ui/password/Authentication.ts +8 -0
- package/ui/src/@/iam/ui/password/Form.svelte +151 -0
- package/ui/src/@/iam/ui/password/Form.ts +6 -0
- package/ui/src/@/iam/ui/password/Password.svelte +39 -0
- package/ui/src/@/iam/ui/password/Password.ts +6 -0
- package/ui/src/@/iam/ui/password/Refresh.svelte +5 -0
- package/ui/src/@/iam/ui/password/index.ts +2 -0
- package/ui/src/@/net/index.ts +1 -0
- package/ui/src/@/net/svc/index.ts +3 -0
- package/ui/src/@/net/svc/origin.ts +8 -0
- package/ui/src/@/net/svc/query.test.ts +45 -0
- package/ui/src/@/net/svc/query.ts +25 -0
- package/ui/src/@/passkeys/index.ts +1 -0
- package/ui/src/@/passkeys/svc/add.ts +15 -0
- package/ui/src/@/passkeys/svc/create.ts +58 -0
- package/ui/src/@/passkeys/svc/credentials/convert.ts +32 -0
- package/ui/src/@/passkeys/svc/credentials/create.ts +34 -0
- package/ui/src/@/passkeys/svc/credentials/get.ts +14 -0
- package/ui/src/@/passkeys/svc/credentials/index.ts +3 -0
- package/ui/src/@/passkeys/svc/credentials/key.ts +10 -0
- package/ui/src/@/passkeys/svc/get.ts +10 -0
- package/ui/src/@/passkeys/svc/index.ts +6 -0
- package/ui/src/@/passkeys/svc/net/CreationResponse.ts +15 -0
- package/ui/src/@/passkeys/svc/net/Passkey.ts +8 -0
- package/ui/src/@/passkeys/svc/net/RequestResponse.ts +12 -0
- package/ui/src/@/passkeys/svc/net/challenges/Options.ts +21 -0
- package/ui/src/@/passkeys/svc/net/challenges/index.ts +2 -0
- package/ui/src/@/passkeys/svc/net/challenges/interface.ts +16 -0
- package/ui/src/@/passkeys/svc/net/index.ts +4 -0
- package/ui/src/@/passkeys/svc/net/interface.ts +29 -0
- package/ui/src/@/passkeys/svc/remove.ts +13 -0
- package/ui/src/@/passkeys/svc/request.ts +50 -0
- package/ui/src/@/passkeys/svc/store.ts +13 -0
- package/ui/src/@/passkeys/svc/supported.ts +1 -0
- package/ui/src/@/passkeys/ui/aaguid.json +150 -0
- package/ui/src/@/realtime/events.ts +1 -0
- package/ui/src/@/realtime/index.ts +1 -0
- package/ui/src/@/realtime/rc.ts +13 -0
- package/ui/src/@/realtime/svc/connect.ts +67 -0
- package/ui/src/@/realtime/svc/events.ts +17 -0
- package/ui/src/@/realtime/svc/index.ts +5 -0
- package/ui/src/@/realtime/svc/net/Message.ts +6 -0
- package/ui/src/@/realtime/svc/net/index.ts +2 -0
- package/ui/src/@/realtime/svc/net/interface.ts +13 -0
- package/ui/src/@/realtime/svc/reset.ts +5 -0
- package/ui/src/@/realtime/svc/status.ts +12 -0
- package/ui/src/@/realtime/svc/store.ts +30 -0
- package/ui/src/@/realtime/svc/time.ts +15 -0
- package/ui/src/app.d.ts +20 -0
- package/ui/src/app.html +13 -0
- package/ui/src/config/index.ts +70 -0
- package/ui/src/lib/assets/favicon.svg +1 -0
- package/ui/src/lib/components/clipboard/Clipboard.svelte +63 -0
- package/ui/src/lib/components/clipboard/Clipboard.ts +12 -0
- package/ui/src/lib/components/clipboard/index.ts +1 -0
- package/ui/src/lib/components/dismissable/Dismissable.svelte +81 -0
- package/ui/src/lib/components/dismissable/Dismissable.ts +7 -0
- package/ui/src/lib/components/dismissable/index.ts +1 -0
- package/ui/src/lib/components/dropdown/Back.svelte +14 -0
- package/ui/src/lib/components/dropdown/Back.ts +3 -0
- package/ui/src/lib/components/dropdown/Content.svelte +76 -0
- package/ui/src/lib/components/dropdown/Content.ts +13 -0
- package/ui/src/lib/components/dropdown/Context.ts +18 -0
- package/ui/src/lib/components/dropdown/Group.svelte +24 -0
- package/ui/src/lib/components/dropdown/Group.ts +8 -0
- package/ui/src/lib/components/dropdown/GroupContext.ts +15 -0
- package/ui/src/lib/components/dropdown/Item.svelte +31 -0
- package/ui/src/lib/components/dropdown/Item.ts +5 -0
- package/ui/src/lib/components/dropdown/Layer.svelte +11 -0
- package/ui/src/lib/components/dropdown/Layer.ts +7 -0
- package/ui/src/lib/components/dropdown/Root.svelte +88 -0
- package/ui/src/lib/components/dropdown/Root.ts +6 -0
- package/ui/src/lib/components/dropdown/Separator.svelte +13 -0
- package/ui/src/lib/components/dropdown/Separator.ts +5 -0
- package/ui/src/lib/components/dropdown/Trigger.svelte +33 -0
- package/ui/src/lib/components/dropdown/Trigger.ts +5 -0
- package/ui/src/lib/components/dropdown/index.ts +8 -0
- package/ui/src/lib/components/fullscreen/Fullscreen.svelte +97 -0
- package/ui/src/lib/components/fullscreen/Fullscreen.ts +18 -0
- package/ui/src/lib/components/fullscreen/Overlay.svelte +15 -0
- package/ui/src/lib/components/fullscreen/Overlay.ts +7 -0
- package/ui/src/lib/components/fullscreen/index.ts +2 -0
- package/ui/src/lib/components/hold/Hold.svelte +133 -0
- package/ui/src/lib/components/hold/Hold.ts +23 -0
- package/ui/src/lib/components/hold/index.ts +1 -0
- package/ui/src/lib/components/scrollable/Scrollable.svelte +49 -0
- package/ui/src/lib/components/scrollable/Scrollable.ts +117 -0
- package/ui/src/lib/components/scrollable/index.ts +2 -0
- package/ui/src/lib/components/shake/Shake.svelte +62 -0
- package/ui/src/lib/components/shake/Shake.ts +7 -0
- package/ui/src/lib/components/shake/index.ts +1 -0
- package/ui/src/lib/components/share/Share.svelte +58 -0
- package/ui/src/lib/components/share/Share.ts +9 -0
- package/ui/src/lib/components/share/index.ts +1 -0
- package/ui/src/lib/components/shell/Attention.svelte +11 -0
- package/ui/src/lib/components/shell/Attention.ts +6 -0
- package/ui/src/lib/components/shell/Header.svelte +18 -0
- package/ui/src/lib/components/shell/Header.ts +7 -0
- package/ui/src/lib/components/shell/Screen.svelte +28 -0
- package/ui/src/lib/components/shell/Screen.ts +11 -0
- package/ui/src/lib/components/shell/Sticky.svelte +23 -0
- package/ui/src/lib/components/shell/Sticky.ts +8 -0
- package/ui/src/lib/components/shell/Underlay.svelte +21 -0
- package/ui/src/lib/components/shell/Underlay.ts +6 -0
- package/ui/src/lib/components/shell/index.ts +6 -0
- package/ui/src/lib/components/shell/nav/Actions.svelte +31 -0
- package/ui/src/lib/components/shell/nav/Actions.ts +16 -0
- package/ui/src/lib/components/shell/nav/Back.svelte +20 -0
- package/ui/src/lib/components/shell/nav/Back.ts +9 -0
- package/ui/src/lib/components/shell/nav/Bar.svelte +24 -0
- package/ui/src/lib/components/shell/nav/Bar.ts +3 -0
- package/ui/src/lib/components/shell/nav/Button.svelte +64 -0
- package/ui/src/lib/components/shell/nav/Button.ts +7 -0
- package/ui/src/lib/components/shell/nav/Nav.svelte +62 -0
- package/ui/src/lib/components/shell/nav/Nav.ts +35 -0
- package/ui/src/lib/components/shell/nav/Return.svelte +29 -0
- package/ui/src/lib/components/shell/nav/Return.ts +15 -0
- package/ui/src/lib/components/shell/nav/Sections.svelte +58 -0
- package/ui/src/lib/components/shell/nav/Sections.ts +6 -0
- package/ui/src/lib/components/shell/nav/Toolbar.svelte +73 -0
- package/ui/src/lib/components/shell/nav/Toolbar.ts +3 -0
- package/ui/src/lib/components/shell/nav/history.ts +36 -0
- package/ui/src/lib/components/shell/nav/index.ts +5 -0
- package/ui/src/lib/components/shell/nav/store.ts +24 -0
- package/ui/src/lib/components/ui/alert/alert-action.svelte +20 -0
- package/ui/src/lib/components/ui/alert/alert-description.svelte +23 -0
- package/ui/src/lib/components/ui/alert/alert-title.svelte +23 -0
- package/ui/src/lib/components/ui/alert/alert.svelte +43 -0
- package/ui/src/lib/components/ui/alert/index.ts +17 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +27 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +27 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +32 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte +17 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte +23 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-media.svelte +20 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +17 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte +7 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte +17 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog-trigger.svelte +7 -0
- package/ui/src/lib/components/ui/alert-dialog/alert-dialog.svelte +7 -0
- package/ui/src/lib/components/ui/alert-dialog/index.ts +40 -0
- package/ui/src/lib/components/ui/badge/badge.svelte +49 -0
- package/ui/src/lib/components/ui/badge/index.ts +2 -0
- package/ui/src/lib/components/ui/button/button.svelte +82 -0
- package/ui/src/lib/components/ui/button/index.ts +17 -0
- package/ui/src/lib/components/ui/button-group/button-group-separator.svelte +23 -0
- package/ui/src/lib/components/ui/button-group/button-group-text.svelte +28 -0
- package/ui/src/lib/components/ui/button-group/button-group.svelte +46 -0
- package/ui/src/lib/components/ui/button-group/index.ts +15 -0
- package/ui/src/lib/components/ui/card/card-action.svelte +23 -0
- package/ui/src/lib/components/ui/card/card-content.svelte +20 -0
- package/ui/src/lib/components/ui/card/card-description.svelte +20 -0
- package/ui/src/lib/components/ui/card/card-footer.svelte +20 -0
- package/ui/src/lib/components/ui/card/card-header.svelte +23 -0
- package/ui/src/lib/components/ui/card/card-title.svelte +20 -0
- package/ui/src/lib/components/ui/card/card.svelte +22 -0
- package/ui/src/lib/components/ui/card/index.ts +25 -0
- package/ui/src/lib/components/ui/checkbox/checkbox.svelte +39 -0
- package/ui/src/lib/components/ui/checkbox/index.ts +6 -0
- package/ui/src/lib/components/ui/collapsible/collapsible-content.svelte +7 -0
- package/ui/src/lib/components/ui/collapsible/collapsible-trigger.svelte +7 -0
- package/ui/src/lib/components/ui/collapsible/collapsible.svelte +20 -0
- package/ui/src/lib/components/ui/collapsible/index.ts +13 -0
- package/ui/src/lib/components/ui/input/index.ts +7 -0
- package/ui/src/lib/components/ui/input/input.svelte +48 -0
- package/ui/src/lib/components/ui/input-otp/index.ts +15 -0
- package/ui/src/lib/components/ui/input-otp/input-otp-group.svelte +20 -0
- package/ui/src/lib/components/ui/input-otp/input-otp-separator.svelte +27 -0
- package/ui/src/lib/components/ui/input-otp/input-otp-slot.svelte +31 -0
- package/ui/src/lib/components/ui/input-otp/input-otp.svelte +23 -0
- package/ui/src/lib/components/ui/item/index.ts +34 -0
- package/ui/src/lib/components/ui/item/item-actions.svelte +20 -0
- package/ui/src/lib/components/ui/item/item-content.svelte +23 -0
- package/ui/src/lib/components/ui/item/item-description.svelte +23 -0
- package/ui/src/lib/components/ui/item/item-footer.svelte +20 -0
- package/ui/src/lib/components/ui/item/item-group.svelte +21 -0
- package/ui/src/lib/components/ui/item/item-header.svelte +20 -0
- package/ui/src/lib/components/ui/item/item-media.svelte +42 -0
- package/ui/src/lib/components/ui/item/item-separator.svelte +19 -0
- package/ui/src/lib/components/ui/item/item-title.svelte +20 -0
- package/ui/src/lib/components/ui/item/item.svelte +61 -0
- package/ui/src/lib/components/ui/kbd/index.ts +10 -0
- package/ui/src/lib/components/ui/kbd/kbd-group.svelte +20 -0
- package/ui/src/lib/components/ui/kbd/kbd.svelte +23 -0
- package/ui/src/lib/components/ui/label/index.ts +7 -0
- package/ui/src/lib/components/ui/label/label.svelte +20 -0
- package/ui/src/lib/components/ui/progress/index.ts +7 -0
- package/ui/src/lib/components/ui/progress/progress.svelte +27 -0
- package/ui/src/lib/components/ui/select/index.ts +37 -0
- package/ui/src/lib/components/ui/select/select-content.svelte +45 -0
- package/ui/src/lib/components/ui/select/select-group-heading.svelte +21 -0
- package/ui/src/lib/components/ui/select/select-group.svelte +17 -0
- package/ui/src/lib/components/ui/select/select-item.svelte +40 -0
- package/ui/src/lib/components/ui/select/select-label.svelte +20 -0
- package/ui/src/lib/components/ui/select/select-portal.svelte +7 -0
- package/ui/src/lib/components/ui/select/select-scroll-down-button.svelte +20 -0
- package/ui/src/lib/components/ui/select/select-scroll-up-button.svelte +20 -0
- package/ui/src/lib/components/ui/select/select-separator.svelte +18 -0
- package/ui/src/lib/components/ui/select/select-trigger.svelte +29 -0
- package/ui/src/lib/components/ui/select/select.svelte +11 -0
- package/ui/src/lib/components/ui/separator/index.ts +7 -0
- package/ui/src/lib/components/ui/separator/separator.svelte +23 -0
- package/ui/src/lib/components/ui/spinner/index.ts +1 -0
- package/ui/src/lib/components/ui/spinner/spinner.svelte +18 -0
- package/ui/src/lib/components/ui/tabs/index.ts +18 -0
- package/ui/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
- package/ui/src/lib/components/ui/tabs/tabs-list.svelte +40 -0
- package/ui/src/lib/components/ui/tabs/tabs-trigger.svelte +23 -0
- package/ui/src/lib/components/ui/tabs/tabs.svelte +19 -0
- package/ui/src/lib/components/ui/textarea/index.ts +7 -0
- package/ui/src/lib/components/ui/textarea/textarea.svelte +22 -0
- package/ui/src/lib/intl/bcp.ts +25 -0
- package/ui/src/lib/intl/built.js +18 -0
- package/ui/src/lib/intl/context.yaml +18 -0
- package/ui/src/lib/intl/en-US.yaml +6 -0
- package/ui/src/lib/intl/index.ts +65 -0
- package/ui/src/lib/intl/types.ts +19 -0
- package/ui/src/lib/tools/ago.ts +10 -0
- package/ui/src/lib/tools/assert.ts +3 -0
- package/ui/src/lib/tools/cap.ts +5 -0
- package/ui/src/lib/tools/clicks.ts +25 -0
- package/ui/src/lib/tools/contextmenu.ts +19 -0
- package/ui/src/lib/tools/convert.ts +32 -0
- package/ui/src/lib/tools/currency.test.ts +10 -0
- package/ui/src/lib/tools/currency.ts +55 -0
- package/ui/src/lib/tools/date.test.ts +21 -0
- package/ui/src/lib/tools/date.ts +35 -0
- package/ui/src/lib/tools/delay.ts +7 -0
- package/ui/src/lib/tools/image.ts +39 -0
- package/ui/src/lib/tools/index.ts +20 -0
- package/ui/src/lib/tools/location.ts +41 -0
- package/ui/src/lib/tools/metronome.ts +11 -0
- package/ui/src/lib/tools/mq.ts +84 -0
- package/ui/src/lib/tools/onsubmit.ts +21 -0
- package/ui/src/lib/tools/random.ts +15 -0
- package/ui/src/lib/tools/search.ts +25 -0
- package/ui/src/lib/tools/timeout.ts +26 -0
- package/ui/src/lib/tools/transition.ts +207 -0
- package/ui/src/lib/tools/types.ts +3 -0
- package/ui/src/lib/tools/uuid.test.ts +28 -0
- package/ui/src/lib/tools/uuid.ts +9 -0
- package/ui/src/lib/typeset.css +502 -0
- package/ui/src/lib/utils.ts +12 -0
- package/ui/src/routes/(private)/+layout.svelte +112 -0
- package/ui/src/routes/(private)/+page.svelte +7 -0
- package/ui/src/routes/(private)/[component]/+page.svelte +77 -0
- package/ui/src/routes/+layout.svelte +25 -0
- package/ui/src/routes/+layout.ts +14 -0
- package/ui/src/routes/layout.css +232 -0
- package/ui/static/robots.txt +3 -0
- package/ui/svelte.config.js +36 -0
- package/ui/tools/feat.js +43 -0
- package/ui/tools/hooks/README.md +13 -0
- package/ui/tools/hooks/guard-intl.sh +40 -0
- package/ui/tools/shad.js +7 -0
- package/ui/tsconfig.json +20 -0
- package/ui/vite.config.ts +8 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Domain Organization
|
|
4
|
+
|
|
5
|
+
The codebase follows a domain-driven design pattern with each domain organized under `src/@/`. Each domain contains standardized layers:
|
|
6
|
+
|
|
7
|
+
### Domain Structure Pattern
|
|
8
|
+
|
|
9
|
+
```plaintext
|
|
10
|
+
src/@/{domain}/
|
|
11
|
+
├── index.ts # Main domain entry point
|
|
12
|
+
├── svc/ # Services layer (business logic)
|
|
13
|
+
│ ├── index.ts # Services entry point
|
|
14
|
+
│ ├── store.ts # Data persistence & state management
|
|
15
|
+
│ ├── create.ts # Service methods
|
|
16
|
+
│ ├── net/ # Network layer
|
|
17
|
+
│ │ ├── index.ts # Network exports
|
|
18
|
+
│ │ ├── interface.ts # API resource
|
|
19
|
+
│ │ ├── Events.ts # Realtime event payload types
|
|
20
|
+
│ │ └── {Model}.ts # Network (wire-format) types
|
|
21
|
+
│ └── {Model}.ts # Linked entity
|
|
22
|
+
└── ui/ # UI layer
|
|
23
|
+
├── index.ts # UI exports
|
|
24
|
+
├── ui.ts # Transient UI state
|
|
25
|
+
├── {Component}.svelte # Svelte component
|
|
26
|
+
└── {Component}.ts # Component Props
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Layer Responsibilities
|
|
30
|
+
|
|
31
|
+
**Service Layer (`svc/`)**:
|
|
32
|
+
|
|
33
|
+
- **`store.ts`**: Manages domain state using `svas` collections, handles persistence and data binding
|
|
34
|
+
- **`create.ts`**: Service methods
|
|
35
|
+
- **`index.ts`**: Exports public service APIs
|
|
36
|
+
|
|
37
|
+
Service methods that rely on the current identity must use `await having()` from the `svas` library for safe operations (reads), and `ensure()` for unsafe ones—because reads can happen during app startup, while the current identity is initialized asynchronously, and unsafe operations are a side effect of a user action.
|
|
38
|
+
|
|
39
|
+
**Network Layer (`svc/net/`)**:
|
|
40
|
+
|
|
41
|
+
- **`net/interface.ts`**: Defines API interfaces using Resource pattern for HTTP communication
|
|
42
|
+
- **`net/{Model}.ts`**: Contains TypeScript interfaces, types, and constants for domain models
|
|
43
|
+
- **`index.ts`**: Exports public service APIs
|
|
44
|
+
- Service layer MUST NOT export network layer
|
|
45
|
+
- Entity type must be exported from `store.ts` even if is unchanged compared to the network layer
|
|
46
|
+
|
|
47
|
+
**UI Layer (`ui/`)**:
|
|
48
|
+
|
|
49
|
+
- Contains Svelte components that consume services
|
|
50
|
+
- Components import domain services via `@/{domain}` alias
|
|
51
|
+
- **`index.ts`**: Exports reusable UI components
|
|
52
|
+
- Transient UI state is managed in `ui.ts` file
|
|
53
|
+
- Active elements must have an `id` attribute that follows the pattern `{domain-or-route}-{element}-{kind}` (e.g. `iam-email-tab`, `accounts-name-input`, `me-logout-button`)
|
|
54
|
+
|
|
55
|
+
### App Domain
|
|
56
|
+
|
|
57
|
+
Some UI components are intended to be reused across multiple screens within this application, but their implementation is still application-bound (e.g., depends on app routing, app stores, domain models, product rules, or app-specific copy/branding). These components should live in the dedicated `app` domain.
|
|
58
|
+
|
|
59
|
+
### Aggregation Pattern
|
|
60
|
+
|
|
61
|
+
When entities require calculated properties that depend on data from multiple domains, use a derived store pattern to connect domains and compute enriched entities:
|
|
62
|
+
|
|
63
|
+
1. **Base Collection Store** (`internal`): Raw domain data persisted via `svas` collection
|
|
64
|
+
2. **Derived Aggregation Store**: Combines multiple stores using Svelte's `derived()` to compute enriched entities
|
|
65
|
+
3. **Pure Computation Functions**: Extract computation logic into pure functions that take all dependencies as parameters
|
|
66
|
+
|
|
67
|
+
#### Example Implementation
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// Base collection store
|
|
71
|
+
export const internal = collection<Group>({
|
|
72
|
+
get,
|
|
73
|
+
persist: 'groups',
|
|
74
|
+
bind: account,
|
|
75
|
+
stale: true,
|
|
76
|
+
values: values<Group>(),
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Derived store aggregating multiple domains
|
|
80
|
+
const groups = derived([internal, contacts, account], ([$internal, $contacts, $account]) => {
|
|
81
|
+
return $internal.map((group) => {
|
|
82
|
+
return {
|
|
83
|
+
...group,
|
|
84
|
+
balance: balance($group, $contacts, $account),
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Shared Components
|
|
91
|
+
|
|
92
|
+
Shared components are app-agnostic UI building blocks. Everything in `src/lib/components/` must be reusable across different apps without modification.
|
|
93
|
+
|
|
94
|
+
A component belongs here only if its implementation is not bound to the current application, meaning it:
|
|
95
|
+
• does not depend on app-specific routes, screens, or domain concepts
|
|
96
|
+
• does not import from domains
|
|
97
|
+
• does not embed product copy, branding, or business rules
|
|
98
|
+
• exposes configuration via props / slots / events, rather than hardcoding behavior
|
|
99
|
+
|
|
100
|
+
```plaintext
|
|
101
|
+
src/lib/components/
|
|
102
|
+
├── section/
|
|
103
|
+
│ ├── index.ts
|
|
104
|
+
│ ├── Section.svelte
|
|
105
|
+
│ └── Section.ts
|
|
106
|
+
└── shell/
|
|
107
|
+
├── index.ts
|
|
108
|
+
├── Nav.svelte
|
|
109
|
+
├── Nav.ts
|
|
110
|
+
├── Screen.svelte
|
|
111
|
+
└── Screen.ts
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Components
|
|
115
|
+
|
|
116
|
+
### Component File
|
|
117
|
+
|
|
118
|
+
- **`{Component}.ts` is the component's TypeScript sidecar**, next to the `.svelte` file. It holds the component's types, constants, and logic — anything the markup doesn't need to state itself. A component with nothing to put there skips the file.
|
|
119
|
+
- **`Props` lives in `{Component}.ts`** — never declared inside `<script>`. A component with no own props still has the file whenever it owns constants or logic worth lifting out of the markup.
|
|
120
|
+
- **Props extend the root** — `interface Props extends RootProps`, where root is the HTML element or component rendered at the top level. Forward `class` and `{...props}` onto it. Nested-element props take distinct, non-conflicting names.
|
|
121
|
+
- **`class` prop is always `ClassValue`** (from `svelte/elements`), never `string`. Optional props document their default with a JSDoc `@default`.
|
|
122
|
+
- **EntityLike props** — components accepting entities declare an `EntityLike` interface (`Pick<Entity, …>`) with only the properties they use, to reduce coupling across domains.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import type { ClassValue } from 'svelte/elements'
|
|
126
|
+
|
|
127
|
+
type AccountLike = Pick<Account, 'name' | 'picture'>
|
|
128
|
+
|
|
129
|
+
export interface Props {
|
|
130
|
+
account: AccountLike
|
|
131
|
+
/** @default 'active' */
|
|
132
|
+
variant?: 'active' | 'inactive'
|
|
133
|
+
class?: ClassValue
|
|
134
|
+
onsubmit?: (value: Value) => Promise<void | Error>
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Decomposition
|
|
139
|
+
|
|
140
|
+
- **Split at >50 lines** (or 2+ snippet props) into smaller components.
|
|
141
|
+
- **Composite** (default): one public root exported as default, its parts internal.
|
|
142
|
+
- **Compound**: only when the composition itself is public API.
|
|
143
|
+
- **Props down, callbacks up** — parent owns business logic, child owns UI. Form components never call services.
|
|
144
|
+
- **Reuse before create** — check `src/lib/components` and `src/@/app/ui` index files before adding anything new.
|
|
145
|
+
|
|
146
|
+
### Organization
|
|
147
|
+
|
|
148
|
+
- **Order within `<script>`**: imports → state → functions → lifecycle. Define before use; pass functions directly (no arrow wrappers).
|
|
149
|
+
- **Naming**: one-word, meaningful. Full words over abbreviations; synonyms and analogies allowed.
|
|
150
|
+
|
|
151
|
+
## Route Areas
|
|
152
|
+
|
|
153
|
+
Routes are grouped under `src/routes/` by access:
|
|
154
|
+
|
|
155
|
+
- **`(public)`** — Everything publicly accessible, typically long-term cacheable. The group layout sets `Cache-Control` to 1 hour.
|
|
156
|
+
- **`(private)`** — Requires authentication.
|
|
157
|
+
- **`(local)`** — For local development.
|
|
158
|
+
|
|
159
|
+
## Screens
|
|
160
|
+
|
|
161
|
+
Shell `Header` (`$com/shell`) is owned by the **page** (`src/routes/**/+page.svelte`), never by domain or shared UI components. The page owns the title and explorer badges as `Header` children. Header utility buttons register via `<Utilities>` (stack; last wins), the same pattern as `<Actions>` for the bottom nav. Presentational components receive props and render body content only.
|
|
162
|
+
|
|
163
|
+
Card / Dialog `*.Header` parts are unrelated — they stay inside those compound components.
|
|
164
|
+
|
|
165
|
+
## Forms Pattern
|
|
166
|
+
|
|
167
|
+
Forms should follow a clear separation of concerns between data collection, validation, and business logic execution.
|
|
168
|
+
|
|
169
|
+
### Form Components
|
|
170
|
+
|
|
171
|
+
**Form Declaration**:
|
|
172
|
+
|
|
173
|
+
- Forms should declare a `Value` interface representing the form data structure
|
|
174
|
+
- Forms should accept an `onsubmit` callback prop for handling form submission
|
|
175
|
+
- Forms should not directly interact with domain services or perform business operations
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
export interface Value {
|
|
179
|
+
title: string
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface Props {
|
|
183
|
+
value?: Value
|
|
184
|
+
onsubmit?: (value: Value) => Promise<void | Error>
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Service Connector
|
|
189
|
+
|
|
190
|
+
Bridge forms with domain services by implementing specific business operations (Create, Edit, Update, etc.)
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// Create.svelte - connects Form to something.add service
|
|
194
|
+
<script lang="ts">
|
|
195
|
+
import { add } from '@/something'
|
|
196
|
+
import { Form, type Value } from './form'
|
|
197
|
+
|
|
198
|
+
let value: Value = $state({ title: '' })
|
|
199
|
+
|
|
200
|
+
async function onsubmit(value: Value) {
|
|
201
|
+
return await add(value)
|
|
202
|
+
}
|
|
203
|
+
</script>
|
|
204
|
+
|
|
205
|
+
<Form {value} {onsubmit} />
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Svelte
|
|
209
|
+
|
|
210
|
+
### Reactivity
|
|
211
|
+
|
|
212
|
+
- **`$derived` over `$effect`** - `$effect` is almost always a symptom of bad architecture. Prefer reactive declarations. If you reach for `$effect`, reconsider the design and find a declarative solution.
|
|
213
|
+
- **Busy state** - Guard async operations against double-clicks with a `busy` flag; disable controls while it's set.
|
|
214
|
+
|
|
215
|
+
```svelte
|
|
216
|
+
<script lang="ts">
|
|
217
|
+
let busy = $state(false)
|
|
218
|
+
|
|
219
|
+
async function save() {
|
|
220
|
+
busy = true
|
|
221
|
+
const result = await add(value)
|
|
222
|
+
busy = false
|
|
223
|
+
if (result instanceof Error) return
|
|
224
|
+
goto('/success')
|
|
225
|
+
}
|
|
226
|
+
</script>
|
|
227
|
+
<Button disabled={busy} onclick={save}>Save</Button>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Persistent State
|
|
231
|
+
|
|
232
|
+
- **Prefer `svas` for app and component state** - For state that outlives a session, `svas` stores (see `/svas`) are our architectural default: `bind:` and reactivity come out of the box. Raw `localStorage` isn't forbidden, but for application and component state `svas` is preferred.
|
|
233
|
+
|
|
234
|
+
### Navigation
|
|
235
|
+
|
|
236
|
+
- **Never** use `resolve` from `$app/navigation`.
|
|
237
|
+
|
|
238
|
+
## Styling
|
|
239
|
+
|
|
240
|
+
### Array Class Syntax
|
|
241
|
+
|
|
242
|
+
Use Svelte array syntax for conditional classes (NOT cn utility):
|
|
243
|
+
|
|
244
|
+
```svelte
|
|
245
|
+
<!-- ✅ Preferred: Array syntax -->
|
|
246
|
+
<Button class={['w-full', isActive && 'bg-primary']}>
|
|
247
|
+
Click me
|
|
248
|
+
</Button>
|
|
249
|
+
|
|
250
|
+
<div class={['flex items-center', expanded && 'bg-muted', className]}>
|
|
251
|
+
Content
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<!-- ❌ Avoid: cn utility (older pattern) -->
|
|
255
|
+
<Button class={cn('w-full', isActive && 'bg-primary')}>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Discipline
|
|
259
|
+
|
|
260
|
+
- **Component styles live with the component** - put a component's own CSS in its `<style>` block, not in global `layout.css`.
|
|
261
|
+
- **Prefer semantic colors** - reach for `bg-card`, `text-muted-foreground`, `border-input` before raw palette values like `bg-gray-100`.
|
|
262
|
+
- **RTL-safe spacing** - use `ms-*`/`me-*`/`start-*`/`end-*`, never `ml-*`/`mr-*`/`left-*`/`right-*` for logical spacing.
|
|
263
|
+
- **No layout shift** - give elements explicit dimensions or aspect-ratio. Intrinsic image size must never determine geometry.
|
|
264
|
+
|
|
265
|
+
## Internationalization (i18n)
|
|
266
|
+
|
|
267
|
+
This project uses `svintl` (`/svintl`, `npx intl`) for all user-facing text.
|
|
268
|
+
|
|
269
|
+
**Every string a user reads goes through intl (`$dict`), never hardcoded** — regardless of how many locales exist or how much text there is to route.
|
|
270
|
+
|
|
271
|
+
**svintl translates automatically** — author the source text once; every other locale is generated. Never supply per-locale translations or code a default-language fallback.
|
|
272
|
+
|
|
273
|
+
Each domain carries its own `ui/intl/` mount whose dictionary derives from the host `$lib/intl` locale and uses **only its own** dictionary.
|
|
274
|
+
|
|
275
|
+
## Linked Entities Pattern
|
|
276
|
+
|
|
277
|
+
The Linked Entities Pattern is used to create rich domain models by composing data from multiple domains. This pattern enhances network-defined entities with related domain data, creating a more complete representation for application use.
|
|
278
|
+
|
|
279
|
+
### Pattern Overview
|
|
280
|
+
|
|
281
|
+
A linked entity extends a network model (from `net/`) with references to related entities from other domains:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// Network model (raw API data) - defined in svc/net/Contact.ts
|
|
285
|
+
export interface Contact {
|
|
286
|
+
id: string
|
|
287
|
+
identity: string
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Linked entity (enriched domain model) - defined in svc/Contact.ts
|
|
291
|
+
export interface Contact extends net.Contact {
|
|
292
|
+
account: Account // Linked entity from @/account domain
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Implementation Pattern
|
|
297
|
+
|
|
298
|
+
**Mapping Function (`map.ts`)**:
|
|
299
|
+
|
|
300
|
+
The mapping function transforms network entities into linked entities by:
|
|
301
|
+
|
|
302
|
+
1. Extracting relevant data from the network model
|
|
303
|
+
2. Fetching related entities from other domains
|
|
304
|
+
3. Combining them into a rich domain model
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import { awaited } from 'svas'
|
|
308
|
+
import { accounts } from '@/accounts'
|
|
309
|
+
|
|
310
|
+
export async function map(entry: net.Contact): Promise<Contact | Error> {
|
|
311
|
+
const account = await awaited(accounts.get(entry.identity)) // Fetch linked Account entity
|
|
312
|
+
|
|
313
|
+
if (account instanceof Error) return account
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
...entry,
|
|
317
|
+
account, // Linked Account entity from @/account domain
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Store Integration (`store.ts`)**:
|
|
323
|
+
|
|
324
|
+
The domain store uses the mapping function to transform network data into linked entities:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
export const internal = collection<Contact>({ get })
|
|
328
|
+
|
|
329
|
+
events.on('default.contacts.sync', async (entry: net.Contact) => {
|
|
330
|
+
const contact = await map(entry)
|
|
331
|
+
|
|
332
|
+
if (contact instanceof Error) return
|
|
333
|
+
|
|
334
|
+
sync(internal, contact)
|
|
335
|
+
})
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Benefits
|
|
339
|
+
|
|
340
|
+
- **Separation of Concerns**: Network models (`net/`) remain clean API representations
|
|
341
|
+
- **Rich Domain Models**: Application logic works with complete, meaningful entities
|
|
342
|
+
- **Reusability**: Linked entities can reference shared domain models (e.g., `Account`)
|
|
343
|
+
- **Maintainability**: Changes to related domains are automatically reflected through references
|
|
344
|
+
|
|
345
|
+
## Static assets
|
|
346
|
+
|
|
347
|
+
Static assets must be stored in the `static/` directory or inside the component directory.
|
|
348
|
+
|
|
349
|
+
> Assets in the `routes/` directory may create multiple URLs for the same asset, which breaks caching.
|
|
350
|
+
|
|
351
|
+
Optimize images using `npx sharp --optimize`.
|
|
352
|
+
|
|
353
|
+
## Testing
|
|
354
|
+
|
|
355
|
+
Playwright BDD with Gherkin feature files for end-to-end testing.
|
|
356
|
+
|
|
357
|
+
- **Structure** - Feature files (`features/*.feature`), step definitions (`features/steps/*.ts`).
|
|
358
|
+
- **Generic vs domain steps** - Generic, domain-agnostic steps live in `navigaton.ts` (navigation, tapping, visibility, filling) and `typing.ts` (keyboard, random data). Domain-specific logic goes in `{domain}.ts` files. Reach for a generic step first; add a domain one only when there is no generic way to say it.
|
|
359
|
+
- **ID-based selectors** - Test-id pattern `{domain}-{element}-{kind}`. Add IDs when a test needs them, not preemptively.
|
|
360
|
+
- **Comprehensive scenarios** - Prefer fewer, complete scenarios over many small ones. Clean up unused step definitions.
|
|
361
|
+
- **Timeouts aren't solutions** - Fix the flow rather than masking a broken one with `{ timeout }`. Assert on URL and content instead.
|
|
362
|
+
|
|
363
|
+
```gherkin
|
|
364
|
+
Scenario: Create and favorite contact
|
|
365
|
+
Given new account
|
|
366
|
+
When I tap 'nav-contacts-button'
|
|
367
|
+
And I tap 'contacts-new-button'
|
|
368
|
+
And I type random name
|
|
369
|
+
And I tap 'contacts-save-button'
|
|
370
|
+
Then 'contact-name' contains that name
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
When('I tap {string}', async ({ page }, id) => {
|
|
375
|
+
await page.locator(`#${id}`).click()
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
Then('{string} contains that name', async ({ page, ctx }, id) => {
|
|
379
|
+
await expect(page.locator(`#${id}`)).toContainText(ctx.name)
|
|
380
|
+
})
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Running Tests
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
npx feat # all tests
|
|
387
|
+
npx feat features/authentication.feature # specific feature
|
|
388
|
+
npx feat features/authentication.feature --name "scenario name"
|
|
389
|
+
APP_URL=https://evnapp.com npx feat # against production
|
|
390
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://shadcn-svelte.com/schema.json",
|
|
3
|
+
"tailwind": {
|
|
4
|
+
"css": "src/routes/layout.css",
|
|
5
|
+
"baseColor": "neutral"
|
|
6
|
+
},
|
|
7
|
+
"aliases": {
|
|
8
|
+
"components": "$com",
|
|
9
|
+
"utils": "$lib/utils",
|
|
10
|
+
"ui": "$ui",
|
|
11
|
+
"hooks": "$lib/hooks",
|
|
12
|
+
"lib": "$lib"
|
|
13
|
+
},
|
|
14
|
+
"typescript": true,
|
|
15
|
+
"registry": "https://shadcn-svelte.com/registry",
|
|
16
|
+
"style": "nova",
|
|
17
|
+
"iconLibrary": "lucide",
|
|
18
|
+
"menuColor": "default",
|
|
19
|
+
"menuAccent": "subtle"
|
|
20
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import ts from 'typescript-eslint'
|
|
2
|
+
import neostandard from 'neostandard'
|
|
3
|
+
import globals from 'globals'
|
|
4
|
+
import unusedImports from 'eslint-plugin-unused-imports'
|
|
5
|
+
import svelte from 'eslint-plugin-svelte'
|
|
6
|
+
import importPlugin from 'eslint-plugin-import'
|
|
7
|
+
import tsPlugin from '@typescript-eslint/eslint-plugin'
|
|
8
|
+
|
|
9
|
+
export default [
|
|
10
|
+
...neostandard({
|
|
11
|
+
ignores: [
|
|
12
|
+
'.svelte-kit',
|
|
13
|
+
'dist', // the build; ignored here rather than in .gitignore — see that file
|
|
14
|
+
'src/lib/components/ui',
|
|
15
|
+
'**/*.min.js',
|
|
16
|
+
'src/**/intl/**/*',
|
|
17
|
+
...neostandard.resolveIgnoresFromGitignore(),
|
|
18
|
+
],
|
|
19
|
+
ts: true,
|
|
20
|
+
}),
|
|
21
|
+
{
|
|
22
|
+
plugins: {
|
|
23
|
+
import: importPlugin,
|
|
24
|
+
'@typescript-eslint': tsPlugin,
|
|
25
|
+
'unused-imports': unusedImports,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
...svelte.configs['flat/recommended'],
|
|
29
|
+
{
|
|
30
|
+
languageOptions: {
|
|
31
|
+
globals: {
|
|
32
|
+
...globals.browser,
|
|
33
|
+
...globals.node,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
files: ['**/*.svelte'],
|
|
39
|
+
|
|
40
|
+
languageOptions: {
|
|
41
|
+
parserOptions: {
|
|
42
|
+
parser: ts.parser,
|
|
43
|
+
svelteFeatures: {
|
|
44
|
+
experimentalGenerics: true,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
rules: {
|
|
51
|
+
'no-void': 'off',
|
|
52
|
+
curly: ['error', 'multi'],
|
|
53
|
+
'no-console': ['error', { allow: ['info', 'debug', 'warn', 'error'] }],
|
|
54
|
+
'no-implicit-coercion': 'error',
|
|
55
|
+
'@stylistic/comma-dangle': ['error', 'always-multiline'],
|
|
56
|
+
'@stylistic/space-before-function-paren': ['error', {
|
|
57
|
+
asyncArrow: 'always',
|
|
58
|
+
named: 'never',
|
|
59
|
+
anonymous: 'never',
|
|
60
|
+
}],
|
|
61
|
+
'arrow-parens': ['error', 'always'],
|
|
62
|
+
'no-unused-vars': 'off',
|
|
63
|
+
'unused-imports/no-unused-imports': 'error',
|
|
64
|
+
'unused-imports/no-unused-vars': [
|
|
65
|
+
'warn',
|
|
66
|
+
{
|
|
67
|
+
vars: 'all',
|
|
68
|
+
varsIgnorePattern: '^_',
|
|
69
|
+
args: 'after-used',
|
|
70
|
+
argsIgnorePattern: '^_',
|
|
71
|
+
}],
|
|
72
|
+
'@stylistic/padding-line-between-statements': [
|
|
73
|
+
'error',
|
|
74
|
+
{
|
|
75
|
+
blankLine: 'always',
|
|
76
|
+
prev: ['block-like', 'if', 'multiline-expression'],
|
|
77
|
+
next: '*',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
blankLine: 'always',
|
|
81
|
+
prev: '*',
|
|
82
|
+
next: ['block-like', 'if', 'multiline-expression'],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
blankLine: 'always',
|
|
86
|
+
prev: ['const', 'let'],
|
|
87
|
+
next: ['expression', 'for'],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
blankLine: 'always',
|
|
91
|
+
prev: 'expression',
|
|
92
|
+
next: ['const', 'let'],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
blankLine: 'always',
|
|
96
|
+
prev: ['multiline-const', 'multiline-let'],
|
|
97
|
+
next: '*',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
blankLine: 'always',
|
|
101
|
+
prev: '*',
|
|
102
|
+
next: ['multiline-const', 'multiline-let'],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
blankLine: 'always',
|
|
106
|
+
prev: '*',
|
|
107
|
+
next: 'return',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
blankLine: 'always',
|
|
111
|
+
prev: '*',
|
|
112
|
+
next: 'break',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
blankLine: 'always',
|
|
116
|
+
prev: '*',
|
|
117
|
+
next: 'continue',
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
'import/order': ['error', {
|
|
121
|
+
groups: ['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling', 'index', 'type'],
|
|
122
|
+
alphabetize: {
|
|
123
|
+
order: 'desc',
|
|
124
|
+
},
|
|
125
|
+
}],
|
|
126
|
+
'import-x/no-duplicates': 'off', // stupid shit doesn't work
|
|
127
|
+
'no-warning-comments': ['error', {
|
|
128
|
+
terms: ['todo', 'fixme'],
|
|
129
|
+
location: 'anywhere',
|
|
130
|
+
}],
|
|
131
|
+
'import/newline-after-import': ['error', { count: 1 }],
|
|
132
|
+
'svelte/no-navigation-without-resolve': 'off',
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
files: ['src/**/*.ts', 'features/**/*.ts'],
|
|
137
|
+
ignores: ['src/service-worker.ts'],
|
|
138
|
+
languageOptions: {
|
|
139
|
+
parserOptions: {
|
|
140
|
+
project: './tsconfig.json',
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
rules: {
|
|
144
|
+
'@typescript-eslint/consistent-type-imports': 'error',
|
|
145
|
+
'@typescript-eslint/strict-boolean-expressions': ['error', {
|
|
146
|
+
allowNullableObject: false,
|
|
147
|
+
allowNumber: false,
|
|
148
|
+
allowString: false,
|
|
149
|
+
}],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createBdd } from 'playwright-bdd'
|
|
2
|
+
import { expect } from '@playwright/test'
|
|
3
|
+
import { faker } from '@faker-js/faker'
|
|
4
|
+
import { BASE } from '../base'
|
|
5
|
+
|
|
6
|
+
const { Given, Then } = createBdd()
|
|
7
|
+
|
|
8
|
+
Given('new account', async ({ page }) => {
|
|
9
|
+
await page.goto(BASE + '/')
|
|
10
|
+
await page.locator('#iam.email-tab').click()
|
|
11
|
+
await expect(page.locator('#iam.username-input')).toBeFocused()
|
|
12
|
+
await page.keyboard.type(faker.internet.email())
|
|
13
|
+
await page.keyboard.press('Tab')
|
|
14
|
+
await page.keyboard.type(faker.internet.password())
|
|
15
|
+
await page.keyboard.press('Enter')
|
|
16
|
+
|
|
17
|
+
await expect(async () => {
|
|
18
|
+
const challenge = await page.evaluate(() => localStorage.getItem('auth:challenge'))
|
|
19
|
+
|
|
20
|
+
expect(challenge).not.toBeNull()
|
|
21
|
+
}).toPass()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
Then('I am authenticated', async ({ page }) => {
|
|
25
|
+
const challenge = await page.evaluate(() => localStorage.getItem('auth:challenge'))
|
|
26
|
+
|
|
27
|
+
expect(challenge).not.toBeNull()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
Then('I am not authenticated', async ({ page }) => {
|
|
31
|
+
const challenge = await page.evaluate(() => localStorage.getItem('auth:challenge'))
|
|
32
|
+
|
|
33
|
+
expect(challenge).toBeNull()
|
|
34
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createBdd } from 'playwright-bdd'
|
|
2
|
+
import { expect } from '@playwright/test'
|
|
3
|
+
import { BASE } from '../base'
|
|
4
|
+
|
|
5
|
+
const { Given, When, Then } = createBdd()
|
|
6
|
+
|
|
7
|
+
Given('path {string}', async ({ page }, path) => {
|
|
8
|
+
await page.goto(BASE + path)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
Then('the page is loaded', async ({ page }) => {
|
|
12
|
+
await expect(page).toHaveURL(/.+/)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
When('I tap {string}', async ({ page }, id) => {
|
|
16
|
+
const element = page.locator(`#${id}`)
|
|
17
|
+
|
|
18
|
+
await expect(element).toBeVisible()
|
|
19
|
+
await element.click()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
When('I hold {string} for {int}s', async ({ page }, id, duration) => {
|
|
23
|
+
const element = page.locator(`#${id}`)
|
|
24
|
+
|
|
25
|
+
await expect(element).toBeVisible()
|
|
26
|
+
await element.hover()
|
|
27
|
+
await page.mouse.down()
|
|
28
|
+
await page.waitForTimeout(duration * 1000)
|
|
29
|
+
await page.mouse.up()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
Then('{string} is focused', async ({ page }, id) => {
|
|
33
|
+
const element = page.locator(`#${id}`)
|
|
34
|
+
|
|
35
|
+
await expect(element).toBeVisible()
|
|
36
|
+
await expect(element).toBeFocused()
|
|
37
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createBdd } from 'playwright-bdd'
|
|
2
|
+
import { faker } from '@faker-js/faker'
|
|
3
|
+
|
|
4
|
+
const { When } = createBdd()
|
|
5
|
+
|
|
6
|
+
When('I type {string}', async ({ page }, text) => {
|
|
7
|
+
await page.keyboard.type(text)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
When('I press {string}', async ({ page }, key) => {
|
|
11
|
+
await page.keyboard.press(key)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
When('I type random email', async ({ page }) => {
|
|
15
|
+
await page.keyboard.type(faker.internet.email())
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
When('I type random password', async ({ page }) => {
|
|
19
|
+
await page.keyboard.type(faker.internet.password())
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
When('I type random name', async ({ page }) => {
|
|
23
|
+
await page.keyboard.type(faker.person.firstName())
|
|
24
|
+
})
|