@stacksjs/defaults 0.74.40 → 0.74.42

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.
Files changed (78) hide show
  1. package/ai/skills/stacks-cms/SKILL.md +4 -4
  2. package/ai/skills/stacks-composables/SKILL.md +1 -1
  3. package/ai/skills/stacks-config/SKILL.md +1 -1
  4. package/ai/skills/stacks-dashboard/SKILL.md +1 -1
  5. package/ai/skills/stacks-technical-diagrams/LICENSE +1 -1
  6. package/ai/skills/stacks-technical-diagrams/SKILL.md +257 -229
  7. package/ai/skills/stacks-technical-diagrams/THIRD_PARTY_NOTICES.md +69 -0
  8. package/ai/skills/stacks-technical-diagrams/assets/JetBrainsMono-OFL.txt +93 -0
  9. package/ai/skills/stacks-technical-diagrams/assets/template.html +14588 -929
  10. package/ai/skills/stacks-technical-diagrams/bin/diagrams +10 -0
  11. package/ai/skills/stacks-technical-diagrams/bin/open-artifact.mjs +86 -0
  12. package/ai/skills/stacks-technical-diagrams/bin/preview.mjs +654 -0
  13. package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +1898 -89
  14. package/ai/skills/stacks-technical-diagrams/bin/visual-check.mjs +829 -0
  15. package/ai/skills/stacks-technical-diagrams/brand-marks/README.md +31 -0
  16. package/ai/skills/stacks-technical-diagrams/brand-marks/catalog.json +131 -0
  17. package/ai/skills/stacks-technical-diagrams/delta/architecture-delta.mjs +1221 -0
  18. package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +18 -22
  19. package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +58 -52
  20. package/ai/skills/stacks-technical-diagrams/examples/async-job-roundtrip.sequence.json +61 -0
  21. package/ai/skills/stacks-technical-diagrams/examples/brand-aware-delivery.architecture.json +47 -0
  22. package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +30 -23
  23. package/ai/skills/stacks-technical-diagrams/examples/checkout-platform.base.architecture.json +31 -0
  24. package/ai/skills/stacks-technical-diagrams/examples/checkout-platform.head.architecture.json +31 -0
  25. package/ai/skills/stacks-technical-diagrams/examples/deployment-release.lifecycle.json +49 -0
  26. package/ai/skills/stacks-technical-diagrams/examples/event-stream.dataflow.json +57 -0
  27. package/ai/skills/stacks-technical-diagrams/examples/incident-response.workflow.json +64 -0
  28. package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +22 -16
  29. package/ai/skills/stacks-technical-diagrams/examples/production-deployment.architecture.json +71 -0
  30. package/ai/skills/stacks-technical-diagrams/examples/release-delivery.workflow.json +62 -0
  31. package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +16 -11
  32. package/ai/skills/stacks-technical-diagrams/migrations/workflow-v2.mjs +279 -0
  33. package/ai/skills/stacks-technical-diagrams/recipes/scenarios.mjs +391 -0
  34. package/ai/skills/stacks-technical-diagrams/references/authoring-contract.md +243 -0
  35. package/ai/skills/stacks-technical-diagrams/references/brand-marks.md +65 -0
  36. package/ai/skills/stacks-technical-diagrams/references/delivery-contract.md +120 -0
  37. package/ai/skills/stacks-technical-diagrams/references/viewer-runtime.md +45 -0
  38. package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +780 -73
  39. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +25 -3
  40. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +240 -52
  41. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +31 -7
  42. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +227 -50
  43. package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +36 -6
  44. package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +270 -63
  45. package/ai/skills/stacks-technical-diagrams/renderers/shared/brand-marks.mjs +563 -0
  46. package/ai/skills/stacks-technical-diagrams/renderers/shared/bun-runtime.mjs +20 -0
  47. package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +186 -10
  48. package/ai/skills/stacks-technical-diagrams/renderers/shared/desktop-readability.mjs +26 -0
  49. package/ai/skills/stacks-technical-diagrams/renderers/shared/diagnostics.mjs +127 -0
  50. package/ai/skills/stacks-technical-diagrams/renderers/shared/engineering-profiles.mjs +157 -0
  51. package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-brand-marks.mjs +2003 -0
  52. package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +3 -3
  53. package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +1195 -2
  54. package/ai/skills/stacks-technical-diagrams/renderers/shared/i18n.mjs +595 -0
  55. package/ai/skills/stacks-technical-diagrams/renderers/shared/legend.mjs +217 -0
  56. package/ai/skills/stacks-technical-diagrams/renderers/shared/output-path.mjs +340 -0
  57. package/ai/skills/stacks-technical-diagrams/renderers/shared/repository-evidence.mjs +238 -0
  58. package/ai/skills/stacks-technical-diagrams/renderers/shared/repository-location.mjs +58 -0
  59. package/ai/skills/stacks-technical-diagrams/renderers/shared/text-fit.mjs +49 -0
  60. package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +163 -19
  61. package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +51 -5
  62. package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +137 -17
  63. package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +24 -470
  64. package/ai/skills/stacks-technical-diagrams/renderers/workflow/workflow-compiler.mjs +4400 -0
  65. package/ai/skills/stacks-technical-diagrams/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  66. package/ai/skills/stacks-technical-diagrams/schemas/README.md +154 -11
  67. package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +61 -4
  68. package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +72 -0
  69. package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +40 -18
  70. package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +43 -18
  71. package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +41 -4
  72. package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +97 -1
  73. package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +551 -12
  74. package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +3 -4
  75. package/ide/vscode/package.json +1 -1
  76. package/package.json +2 -2
  77. package/resources/components/CookieConsent.stx +146 -0
  78. package/resources/components/CookieConsent.test.ts +79 -0
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/defaults",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.74.40",
5
+ "version": "0.74.42",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/stacksjs/stacks.git",
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@iconify-json/f7": "^1.2.2",
57
57
  "@iconify-json/hugeicons": "^1.2.27",
58
- "@stacksjs/mobile": "^0.74.40",
58
+ "@stacksjs/mobile": "^0.74.42",
59
59
  "@stacksjs/sanitizer": "^0.2.113",
60
60
  "ts-qr-codes": "^0.1.8"
61
61
  }
@@ -0,0 +1,146 @@
1
+ <script client>
2
+ /**
3
+ * Cookie consent banner (stacksjs/stacks#365).
4
+ *
5
+ * The decision logic is `useCookieConsent` in `@stacksjs/composables`, which
6
+ * is where the rule that matters lives: every optional category is declined
7
+ * until the visitor says otherwise, and there is no way to ask for a
8
+ * permissive default.
9
+ *
10
+ * This is the surface. Three things about it are compliance decisions rather
11
+ * than design ones, and each is easy to get wrong:
12
+ *
13
+ * - **"Decline" is as prominent as "Accept".** A banner where refusing is a
14
+ * greyed-out link and accepting is the primary button is the pattern
15
+ * regulators call out, and it is what most implementations ship.
16
+ * - **Nothing is preselected.** The per-category toggles start off, because
17
+ * the stored decision starts empty.
18
+ * - **The banner does not block the page.** It is a dialog in the
19
+ * accessibility sense but not a modal that traps the visitor until they
20
+ * choose - consent obtained by making the site unusable is not freely given.
21
+ *
22
+ * `onChange` is where an app loads what a category gates, and where it records
23
+ * a `ConsentEvent` if it wants the audit trail.
24
+ */
25
+
26
+ import type { ConsentCategory } from '@stacksjs/composables'
27
+ import { useCookieConsent } from '@stacksjs/composables'
28
+
29
+ interface CookieConsentProps {
30
+ /**
31
+ * Bump this when the cookie policy changes. Every stored decision under a
32
+ * previous version is discarded and the banner asks again, because consent
33
+ * to a previous policy is not consent to this one.
34
+ * @default '1.0'
35
+ */
36
+ policyVersion?: string
37
+ /** Where "read our policy" points. */
38
+ policyUrl?: string
39
+ /** Heading text. */
40
+ title?: string
41
+ /** Body text. Keep it about what is collected and why. */
42
+ message?: string
43
+ }
44
+
45
+ const {
46
+ policyVersion = '1.0',
47
+ policyUrl = '/privacy',
48
+ title = 'Cookies',
49
+ message = 'We use necessary cookies to make this site work. With your permission we would also like to use optional ones to understand how it is used and to improve it.',
50
+ } = defineProps<CookieConsentProps>()
51
+
52
+ const { needsDecision, allows, acceptAll, declineAll, accept } = useCookieConsent({ policyVersion })
53
+
54
+ // Per-category toggles for the "choose" panel, starting from what is actually
55
+ // consented - which on a first visit is nothing.
56
+ const showChoices = state(false)
57
+ const preferences = state(allows('preferences'))
58
+ const analytics = state(allows('analytics'))
59
+ const marketing = state(allows('marketing'))
60
+
61
+ function saveChoices() {
62
+ const chosen: ConsentCategory[] = []
63
+ if (preferences()) chosen.push('preferences')
64
+ if (analytics()) chosen.push('analytics')
65
+ if (marketing()) chosen.push('marketing')
66
+ accept(chosen)
67
+ }
68
+ </script>
69
+
70
+ <template>
71
+ @if(needsDecision)
72
+ <div
73
+ role="region"
74
+ aria-label="Cookie consent"
75
+ class="fixed bottom-0 inset-x-0 z-50 p-4 sm:p-6"
76
+ >
77
+ <div class="mx-auto max-w-3xl rounded-lg border border-gray-200 bg-white p-4 shadow-lg dark:border-neutral-700 dark:bg-neutral-900 sm:p-6">
78
+ <h2 class="text-base font-semibold text-gray-900 dark:text-white">{{ title }}</h2>
79
+
80
+ <p class="mt-2 text-sm text-gray-600 dark:text-neutral-300">
81
+ {{ message }}
82
+ <a :href="policyUrl" class="underline hover:no-underline">Read our policy</a>.
83
+ </p>
84
+
85
+ @if(showChoices())
86
+ <fieldset class="mt-4 space-y-3">
87
+ <legend class="sr-only">Cookie categories</legend>
88
+
89
+ <label class="flex items-start gap-3 text-sm">
90
+ <input type="checkbox" checked disabled class="mt-0.5" />
91
+ <span>
92
+ <span class="font-medium text-gray-900 dark:text-white">Necessary</span>
93
+ <span class="block text-gray-600 dark:text-neutral-400">Required for the site to work. Cannot be turned off.</span>
94
+ </span>
95
+ </label>
96
+
97
+ <label class="flex items-start gap-3 text-sm">
98
+ <input type="checkbox" :checked="preferences()" @change="preferences.set(!preferences())" class="mt-0.5" />
99
+ <span>
100
+ <span class="font-medium text-gray-900 dark:text-white">Preferences</span>
101
+ <span class="block text-gray-600 dark:text-neutral-400">Remembers choices such as language and theme.</span>
102
+ </span>
103
+ </label>
104
+
105
+ <label class="flex items-start gap-3 text-sm">
106
+ <input type="checkbox" :checked="analytics()" @change="analytics.set(!analytics())" class="mt-0.5" />
107
+ <span>
108
+ <span class="font-medium text-gray-900 dark:text-white">Analytics</span>
109
+ <span class="block text-gray-600 dark:text-neutral-400">Helps us understand how the site is used.</span>
110
+ </span>
111
+ </label>
112
+
113
+ <label class="flex items-start gap-3 text-sm">
114
+ <input type="checkbox" :checked="marketing()" @change="marketing.set(!marketing())" class="mt-0.5" />
115
+ <span>
116
+ <span class="font-medium text-gray-900 dark:text-white">Marketing</span>
117
+ <span class="block text-gray-600 dark:text-neutral-400">Used to measure and target campaigns.</span>
118
+ </span>
119
+ </label>
120
+ </fieldset>
121
+ @endif
122
+
123
+ {{-- Decline is a peer of Accept, not a lesser link. --}}
124
+ <div class="mt-4 flex flex-col gap-2 sm:flex-row sm:justify-end">
125
+ @if(showChoices())
126
+ <button type="button" @click="saveChoices()" class="rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-white hover:bg-gray-700 dark:bg-white dark:text-gray-900 dark:hover:bg-neutral-200">
127
+ Save choices
128
+ </button>
129
+ @else
130
+ <button type="button" @click="showChoices.set(true)" class="rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-50 dark:border-neutral-600 dark:text-white dark:hover:bg-neutral-800">
131
+ Choose
132
+ </button>
133
+ @endif
134
+
135
+ <button type="button" @click="declineAll()" class="rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-50 dark:border-neutral-600 dark:text-white dark:hover:bg-neutral-800">
136
+ Decline
137
+ </button>
138
+
139
+ <button type="button" @click="acceptAll()" class="rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-50 dark:border-neutral-600 dark:text-white dark:hover:bg-neutral-800">
140
+ Accept all
141
+ </button>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ @endif
146
+ </template>
@@ -0,0 +1,79 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { readFileSync } from 'node:fs'
3
+ import { join } from 'node:path'
4
+
5
+ /**
6
+ * The banner's compliance contract (stacksjs/stacks#365).
7
+ *
8
+ * These are the properties a reviewer would check, and each is the kind of
9
+ * thing a later styling pass removes without noticing: the decline button
10
+ * becomes a text link, a checkbox picks up `checked`, the banner gains a
11
+ * backdrop. Asserted against the source because they are facts about the
12
+ * markup, not about the render.
13
+ *
14
+ * The decision logic itself is tested in
15
+ * `core/composables/tests/use-cookie-consent.test.ts`.
16
+ */
17
+
18
+ const source = readFileSync(join(import.meta.dir, 'CookieConsent.stx'), 'utf8')
19
+
20
+ describe('CookieConsent', () => {
21
+ it('gives Decline the same treatment as Accept', () => {
22
+ // A banner where refusing is a greyed-out link and accepting is the
23
+ // primary button is the pattern regulators call out.
24
+ const classesOf = (label: string): string => {
25
+ const match = source.match(new RegExp(`<button[^>]*class="([^"]*)"[^>]*>\\s*${label}`, 's'))
26
+ ?? source.match(new RegExp(`<button[^>]*class="([^"]*)"[^>]*>\\s*\\n\\s*${label}`, 's'))
27
+ return match?.[1] ?? ''
28
+ }
29
+
30
+ const decline = classesOf('Decline')
31
+ const accept = classesOf('Accept all')
32
+
33
+ expect(decline).not.toBe('')
34
+ expect(decline).toBe(accept)
35
+ })
36
+
37
+ it('preselects no optional category', () => {
38
+ // Every optional checkbox binds to state that starts from what is
39
+ // consented, which on a first visit is nothing. A literal `checked` on one
40
+ // of them would silently opt the visitor in.
41
+ const optional = [...source.matchAll(/<input type="checkbox"([^>]*)\/>/g)]
42
+ .map(match => match[1]!)
43
+ .filter(attrs => !attrs.includes('disabled'))
44
+
45
+ expect(optional.length).toBe(3)
46
+ for (const attrs of optional) {
47
+ expect(attrs).toContain(':checked=')
48
+ expect(attrs).not.toMatch(/\schecked[\s/]/)
49
+ }
50
+ })
51
+
52
+ it('marks the necessary category as checked AND disabled', () => {
53
+ // It cannot be declined, so showing it as a live choice would be a lie.
54
+ expect(source).toMatch(/<input type="checkbox" checked disabled/)
55
+ })
56
+
57
+ it('does not trap the visitor in a modal', () => {
58
+ // Consent obtained by making the site unusable is not freely given, so
59
+ // this is a region rather than a modal dialog with a backdrop.
60
+ expect(source).toContain('role="region"')
61
+ expect(source).not.toContain('role="dialog"')
62
+ expect(source).not.toContain('aria-modal')
63
+ })
64
+
65
+ it('labels the region and the category group for screen readers', () => {
66
+ expect(source).toContain('aria-label="Cookie consent"')
67
+ expect(source).toContain('<legend class="sr-only">')
68
+ })
69
+
70
+ it('links to the policy', () => {
71
+ expect(source).toContain(':href="policyUrl"')
72
+ })
73
+
74
+ it('renders nothing once a decision exists', () => {
75
+ // The whole banner is behind `needsDecision`; a visitor who has decided
76
+ // must not see it again on every page.
77
+ expect(source).toMatch(/@if\(needsDecision\)/)
78
+ })
79
+ })