@origostudio/origo-design 0.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.
Files changed (209) hide show
  1. package/.editorconfig +13 -0
  2. package/.gitattributes +22 -0
  3. package/.perf-baseline.json +12 -0
  4. package/.prettierignore +13 -0
  5. package/.prettierrc +10 -0
  6. package/.vscode/extensions.json +3 -0
  7. package/BRANCH-PROTECTION.md +25 -0
  8. package/CHANGELOG.md +292 -0
  9. package/LICENSE +21 -0
  10. package/README.md +11 -0
  11. package/commitlint.config.js +6 -0
  12. package/dist/packages/cli/CHANGELOG.md +92 -0
  13. package/dist/packages/cli/README.md +11 -0
  14. package/dist/packages/cli/package.json +16 -0
  15. package/dist/packages/cli/package.json.bak +16 -0
  16. package/dist/packages/cli/src/commands/generate/entity.d.ts +2 -0
  17. package/dist/packages/cli/src/commands/generate/entity.js +24 -0
  18. package/dist/packages/cli/src/commands/generate/entity.js.map +1 -0
  19. package/dist/packages/cli/src/commands/generate/index.d.ts +2 -0
  20. package/dist/packages/cli/src/commands/generate/index.js +30 -0
  21. package/dist/packages/cli/src/commands/generate/index.js.map +1 -0
  22. package/dist/packages/cli/src/commands/init.d.ts +5 -0
  23. package/dist/packages/cli/src/commands/init.js +27 -0
  24. package/dist/packages/cli/src/commands/init.js.map +1 -0
  25. package/dist/packages/cli/src/commands/new.d.ts +2 -0
  26. package/dist/packages/cli/src/commands/new.js +24 -0
  27. package/dist/packages/cli/src/commands/new.js.map +1 -0
  28. package/dist/packages/cli/src/commands/validate.d.ts +2 -0
  29. package/dist/packages/cli/src/commands/validate.js +50 -0
  30. package/dist/packages/cli/src/commands/validate.js.map +1 -0
  31. package/dist/packages/cli/src/index.d.ts +11 -0
  32. package/dist/packages/cli/src/index.js +15 -0
  33. package/dist/packages/cli/src/index.js.map +1 -0
  34. package/dist/packages/cli/src/lib/cli.d.ts +1 -0
  35. package/dist/packages/cli/src/lib/cli.js +7 -0
  36. package/dist/packages/cli/src/lib/cli.js.map +1 -0
  37. package/dist/packages/cli/src/lib/generator.d.ts +16 -0
  38. package/dist/packages/cli/src/lib/generator.js +165 -0
  39. package/dist/packages/cli/src/lib/generator.js.map +1 -0
  40. package/dist/packages/cli/src/lib/scaffolding.d.ts +5 -0
  41. package/dist/packages/cli/src/lib/scaffolding.js +87 -0
  42. package/dist/packages/cli/src/lib/scaffolding.js.map +1 -0
  43. package/dist/packages/cli/src/lib/validation.d.ts +4 -0
  44. package/dist/packages/cli/src/lib/validation.js +191 -0
  45. package/dist/packages/cli/src/lib/validation.js.map +1 -0
  46. package/dist/packages/cli/src/main.d.ts +4 -0
  47. package/dist/packages/cli/src/main.js +38 -0
  48. package/dist/packages/cli/src/main.js.map +1 -0
  49. package/dist/packages/cli/src/templates/entity.json +6 -0
  50. package/dist/packages/cli/src/templates/extension.json +8 -0
  51. package/dist/packages/cli/src/templates/index.d.ts +29 -0
  52. package/dist/packages/cli/src/templates/index.js +61 -0
  53. package/dist/packages/cli/src/templates/index.js.map +1 -0
  54. package/dist/packages/cli/src/templates/origo.json +16 -0
  55. package/dist/packages/cli/src/utils/errors.d.ts +15 -0
  56. package/dist/packages/cli/src/utils/errors.js +58 -0
  57. package/dist/packages/cli/src/utils/errors.js.map +1 -0
  58. package/dist/packages/core/CHANGELOG.md +275 -0
  59. package/dist/packages/core/README.md +11 -0
  60. package/dist/packages/core/package.json +21 -0
  61. package/dist/packages/core/package.json.bak +21 -0
  62. package/dist/packages/core/src/extension-api/extension-manager.d.ts +35 -0
  63. package/dist/packages/core/src/extension-api/extension-manager.js +300 -0
  64. package/dist/packages/core/src/extension-api/extension-manager.js.map +1 -0
  65. package/dist/packages/core/src/extension-api/index.d.ts +2 -0
  66. package/dist/packages/core/src/extension-api/index.js +6 -0
  67. package/dist/packages/core/src/extension-api/index.js.map +1 -0
  68. package/dist/packages/core/src/extension-api/types.d.ts +34 -0
  69. package/dist/packages/core/src/extension-api/types.js +3 -0
  70. package/dist/packages/core/src/extension-api/types.js.map +1 -0
  71. package/dist/packages/core/src/index.d.ts +3 -0
  72. package/dist/packages/core/src/index.js +7 -0
  73. package/dist/packages/core/src/index.js.map +1 -0
  74. package/dist/packages/core/src/schemas/capability.schema.json +104 -0
  75. package/dist/packages/core/src/schemas/contract.schema.json +52 -0
  76. package/dist/packages/core/src/schemas/domain.schema.json +47 -0
  77. package/dist/packages/core/src/schemas/entity.schema.json +72 -0
  78. package/dist/packages/core/src/schemas/extension.schema.json +39 -0
  79. package/dist/packages/core/src/schemas/permission.schema.json +21 -0
  80. package/dist/packages/core/src/types/ast.d.ts +17 -0
  81. package/dist/packages/core/src/types/ast.js +3 -0
  82. package/dist/packages/core/src/types/ast.js.map +1 -0
  83. package/dist/packages/core/src/types/domain.d.ts +101 -0
  84. package/dist/packages/core/src/types/domain.js +8 -0
  85. package/dist/packages/core/src/types/domain.js.map +1 -0
  86. package/dist/packages/core/src/types/validation.d.ts +12 -0
  87. package/dist/packages/core/src/types/validation.js +16 -0
  88. package/dist/packages/core/src/types/validation.js.map +1 -0
  89. package/dist/packages/core/src/validator/ast-validator.d.ts +11 -0
  90. package/dist/packages/core/src/validator/ast-validator.js +388 -0
  91. package/dist/packages/core/src/validator/ast-validator.js.map +1 -0
  92. package/dist/packages/core/src/validator/index.d.ts +23 -0
  93. package/dist/packages/core/src/validator/index.js +143 -0
  94. package/dist/packages/core/src/validator/index.js.map +1 -0
  95. package/dist/packages/core/src/validator/serializer.d.ts +11 -0
  96. package/dist/packages/core/src/validator/serializer.js +99 -0
  97. package/dist/packages/core/src/validator/serializer.js.map +1 -0
  98. package/eslint.config.js +114 -0
  99. package/generate_prompts.js +19 -0
  100. package/jest.config.ts +5 -0
  101. package/jest.preset.js +3 -0
  102. package/nx.json +169 -0
  103. package/package.json +115 -0
  104. package/packages/angular-renderer/CHANGELOG.md +274 -0
  105. package/packages/angular-renderer/README.md +11 -0
  106. package/packages/angular-renderer/package.json +20 -0
  107. package/packages/angular-renderer/playwright-ct.config.ts +23 -0
  108. package/packages/angular-renderer/src/adapters/web/adapter.ts +145 -0
  109. package/packages/angular-renderer/src/adapters/web/experience-adapter.service.ts +21 -0
  110. package/packages/angular-renderer/src/components/primitives/button/button.component.html +9 -0
  111. package/packages/angular-renderer/src/components/primitives/button/button.component.pw.ts +19 -0
  112. package/packages/angular-renderer/src/components/primitives/button/button.component.scss +31 -0
  113. package/packages/angular-renderer/src/components/primitives/button/button.component.ts +67 -0
  114. package/packages/angular-renderer/src/components/primitives/primitives.a11y.pw.ts +33 -0
  115. package/packages/angular-renderer/src/components/primitives/text-input/text-input.component.html +10 -0
  116. package/packages/angular-renderer/src/components/primitives/text-input/text-input.component.pw.ts +29 -0
  117. package/packages/angular-renderer/src/components/primitives/text-input/text-input.component.scss +42 -0
  118. package/packages/angular-renderer/src/components/primitives/text-input/text-input.component.ts +84 -0
  119. package/packages/angular-renderer/src/components/primitives/vbox/vbox.component.html +2 -0
  120. package/packages/angular-renderer/src/components/primitives/vbox/vbox.component.pw.ts +19 -0
  121. package/packages/angular-renderer/src/components/primitives/vbox/vbox.component.scss +5 -0
  122. package/packages/angular-renderer/src/components/primitives/vbox/vbox.component.ts +74 -0
  123. package/packages/angular-renderer/src/index.ts +7 -0
  124. package/packages/angular-renderer/src/lib/renderer.component.ts +118 -0
  125. package/packages/angular-renderer/src/lib/renderer.tokens.ts +9 -0
  126. package/packages/angular-renderer/src/lib/test-utils.ts +26 -0
  127. package/packages/angular-renderer/src/lib/theme.provider.ts +83 -0
  128. package/packages/angular-renderer/src/test-setup.ts +8 -0
  129. package/packages/cli/CHANGELOG.md +92 -0
  130. package/packages/cli/README.md +11 -0
  131. package/packages/cli/package.json +16 -0
  132. package/packages/cli/src/commands/generate/entity.ts +20 -0
  133. package/packages/cli/src/commands/generate/index.ts +28 -0
  134. package/packages/cli/src/commands/init.ts +28 -0
  135. package/packages/cli/src/commands/new.ts +22 -0
  136. package/packages/cli/src/commands/validate.ts +47 -0
  137. package/packages/cli/src/index.ts +11 -0
  138. package/packages/cli/src/lib/cli.ts +3 -0
  139. package/packages/cli/src/lib/generator.ts +181 -0
  140. package/packages/cli/src/lib/scaffolding.ts +104 -0
  141. package/packages/cli/src/lib/validation.ts +218 -0
  142. package/packages/cli/src/main.ts +38 -0
  143. package/packages/cli/src/templates/entity.json +6 -0
  144. package/packages/cli/src/templates/extension.json +8 -0
  145. package/packages/cli/src/templates/index.ts +94 -0
  146. package/packages/cli/src/templates/origo.json +16 -0
  147. package/packages/cli/src/utils/errors.ts +80 -0
  148. package/packages/core/CHANGELOG.md +275 -0
  149. package/packages/core/README.md +11 -0
  150. package/packages/core/package-lock.json +1035 -0
  151. package/packages/core/package.json +21 -0
  152. package/packages/core/scripts/generate-types.ts +23 -0
  153. package/packages/core/src/extension-api/extension-manager.ts +378 -0
  154. package/packages/core/src/extension-api/index.ts +2 -0
  155. package/packages/core/src/extension-api/types.ts +37 -0
  156. package/packages/core/src/index.ts +3 -0
  157. package/packages/core/src/schemas/__fixtures__/target-page.json +222 -0
  158. package/packages/core/src/schemas/capability.schema.json +104 -0
  159. package/packages/core/src/schemas/contract.schema.json +52 -0
  160. package/packages/core/src/schemas/domain.schema.json +47 -0
  161. package/packages/core/src/schemas/entity.schema.json +72 -0
  162. package/packages/core/src/schemas/extension.schema.json +39 -0
  163. package/packages/core/src/schemas/permission.schema.json +21 -0
  164. package/packages/core/src/types/ast.ts +20 -0
  165. package/packages/core/src/types/domain.ts +105 -0
  166. package/packages/core/src/types/validation.ts +29 -0
  167. package/packages/core/src/validator/ast-validator.ts +406 -0
  168. package/packages/core/src/validator/index.ts +207 -0
  169. package/packages/core/src/validator/serializer.ts +120 -0
  170. package/packages/core/test/fixtures/target-page.json +87 -0
  171. package/packages/design-tokens/CHANGELOG.md +273 -0
  172. package/packages/design-tokens/README.md +11 -0
  173. package/packages/design-tokens/package.json +25 -0
  174. package/packages/design-tokens/src/build.ts +97 -0
  175. package/packages/design-tokens/src/compile.ts +60 -0
  176. package/packages/design-tokens/src/index.ts +7 -0
  177. package/packages/design-tokens/src/runtime/index.ts +2 -0
  178. package/packages/design-tokens/src/runtime/theme-fetcher.ts +90 -0
  179. package/packages/design-tokens/src/runtime/theme-injector.ts +149 -0
  180. package/packages/design-tokens/src/schemas/base-tokens.schema.json +127 -0
  181. package/packages/design-tokens/src/schemas/semantic-tokens.schema.json +52 -0
  182. package/packages/design-tokens/src/tokens/base.json +46 -0
  183. package/packages/design-tokens/src/tokens/semantic.json +33 -0
  184. package/packages/playground/CHANGELOG.md +12 -0
  185. package/packages/playground/package.json +10 -0
  186. package/packages/playground/public/favicon.ico +0 -0
  187. package/packages/playground/src/app/app.component.html +1 -0
  188. package/packages/playground/src/app/app.component.scss +4 -0
  189. package/packages/playground/src/app/app.component.ts +13 -0
  190. package/packages/playground/src/app/app.config.ts +9 -0
  191. package/packages/playground/src/editor/badl-editor.component.html +1 -0
  192. package/packages/playground/src/editor/badl-editor.component.scss +10 -0
  193. package/packages/playground/src/editor/badl-editor.component.ts +57 -0
  194. package/packages/playground/src/editor/editor.worker.ts +1 -0
  195. package/packages/playground/src/editor/json.worker.ts +1 -0
  196. package/packages/playground/src/editor/monaco-environment.ts +11 -0
  197. package/packages/playground/src/editor/schema-registry.ts +30 -0
  198. package/packages/playground/src/index.html +13 -0
  199. package/packages/playground/src/main.ts +5 -0
  200. package/packages/playground/src/schemas/capability.schema.json +104 -0
  201. package/packages/playground/src/schemas/contract.schema.json +52 -0
  202. package/packages/playground/src/schemas/domain.schema.json +47 -0
  203. package/packages/playground/src/schemas/entity.schema.json +72 -0
  204. package/packages/playground/src/schemas/extension.schema.json +39 -0
  205. package/packages/playground/src/schemas/permission.schema.json +21 -0
  206. package/packages/playground/src/styles.scss +8 -0
  207. package/packages/playground/src/test-setup.ts +146 -0
  208. package/packages/playground/vite.config.ts +12 -0
  209. package/packages/playground/vitest.config.mjs +24 -0
@@ -0,0 +1,29 @@
1
+ import { test, expect } from '@playwright/experimental-ct-angular';
2
+ import { TextInputComponent, TextInputProps } from './text-input.component';
3
+ import { InteractionContract } from '@origo/core';
4
+ import AxeBuilder from '@axe-core/playwright';
5
+
6
+ test.describe('TextInputComponent Accessibility', () => {
7
+ test('should not have any automatically detectable accessibility issues', async ({
8
+ mount,
9
+ page,
10
+ }) => {
11
+ await mount(TextInputComponent, {
12
+ props: {
13
+ contract: {
14
+ id: '2',
15
+ type: 'textInput',
16
+ props: {
17
+ placeholder: 'Enter name',
18
+ value: 'Jane',
19
+ 'aria-label': 'Name input',
20
+ 'aria-describedby': 'name-hint',
21
+ },
22
+ } as InteractionContract<TextInputProps>,
23
+ },
24
+ });
25
+
26
+ const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
27
+ expect(accessibilityScanResults.violations).toEqual([]);
28
+ });
29
+ });
@@ -0,0 +1,42 @@
1
+ :host {
2
+ display: inline-block;
3
+ }
4
+ input {
5
+ box-sizing: border-box;
6
+ width: 100%;
7
+ background-color: var(--origo-color-surface-background);
8
+ color: var(--origo-color-text-primary);
9
+ padding: var(--origo-spacing-container-padding);
10
+ border: var(--origo-border-width-default, 1px) solid var(--origo-color-border-default, #ccc);
11
+ border-radius: var(--origo-radius-sm, 4px);
12
+ box-shadow: var(--origo-shadow-sm, none);
13
+ font-family: var(--origo-typography-input-font-family, inherit);
14
+ font-size: var(--origo-typography-input-font-size, inherit);
15
+ font-weight: var(--origo-typography-input-font-weight, inherit);
16
+ line-height: var(--origo-typography-input-line-height, inherit);
17
+ transition: all 0.2s ease-in-out;
18
+ }
19
+ input::placeholder {
20
+ color: var(--origo-color-text-muted, #777);
21
+ }
22
+ input:hover:not(:disabled):not([readonly]) {
23
+ border-color: var(--origo-color-border-hover, #999);
24
+ }
25
+ input:active:not(:disabled):not([readonly]) {
26
+ border-color: var(--origo-color-border-active, #666);
27
+ }
28
+ input:focus,
29
+ input:focus-visible {
30
+ outline: 2px solid var(--origo-color-focus, #005fcc);
31
+ outline-offset: -1px;
32
+ border-color: var(--origo-color-focus, #005fcc);
33
+ }
34
+ input:disabled {
35
+ cursor: not-allowed;
36
+ opacity: var(--origo-opacity-disabled, 0.5);
37
+ background-color: var(--origo-color-surface-disabled, #eee);
38
+ color: var(--origo-color-text-disabled, #999);
39
+ }
40
+ input[readonly] {
41
+ background-color: var(--origo-color-surface-readonly, #f9f9f9);
42
+ }
@@ -0,0 +1,84 @@
1
+ import {
2
+ Component,
3
+ input,
4
+ model,
5
+ ChangeDetectionStrategy,
6
+ computed,
7
+ ViewEncapsulation,
8
+ inject,
9
+ effect,
10
+ untracked,
11
+ SecurityContext,
12
+ } from '@angular/core';
13
+ import { DomSanitizer } from '@angular/platform-browser';
14
+ import { InteractionContract } from '@origo/core';
15
+ import { OrigoAdapter } from '../../../adapters/web/adapter';
16
+ import { WebExperienceAdapterService } from '../../../adapters/web/experience-adapter.service';
17
+
18
+ export interface TextInputProps {
19
+ value?: string;
20
+ placeholder?: string;
21
+ disabled?: boolean;
22
+ readonly?: boolean;
23
+ 'aria-label'?: string;
24
+ 'aria-describedby'?: string;
25
+ }
26
+
27
+ @Component({
28
+ selector: 'origo-text-input',
29
+ standalone: true,
30
+ templateUrl: './text-input.component.html',
31
+ styleUrls: ['./text-input.component.scss'],
32
+ changeDetection: ChangeDetectionStrategy.OnPush,
33
+ encapsulation: ViewEncapsulation.ShadowDom,
34
+ host: {
35
+ '[class.origo-text-input]': 'true',
36
+ },
37
+ })
38
+ export class TextInputComponent implements OrigoAdapter<TextInputProps> {
39
+ static readonly contractSchema = {
40
+ value: 'string',
41
+ placeholder: 'string',
42
+ disabled: 'boolean',
43
+ readonly: 'boolean',
44
+ };
45
+ static readonly strictContract = false;
46
+
47
+ contract = input.required<InteractionContract<TextInputProps>>();
48
+ value = model<string>('');
49
+
50
+ computedPlaceholder = computed(() => this.contract().props?.placeholder ?? '');
51
+ computedDisabled = computed(() => !!this.contract().props?.disabled);
52
+ computedReadonly = computed(() => !!this.contract().props?.readonly);
53
+ computedAriaLabel = computed(() => this.contract().props?.['aria-label'] as string | undefined);
54
+ computedAriaDescribedBy = computed(
55
+ () => this.contract().props?.['aria-describedby'] as string | undefined
56
+ );
57
+
58
+ private experienceAdapter = inject(WebExperienceAdapterService);
59
+ private sanitizer = inject(DomSanitizer);
60
+
61
+ constructor() {
62
+ effect(() => {
63
+ const contractVal = this.contract().props?.value;
64
+ untracked(() =>
65
+ this.value.set(contractVal !== undefined && contractVal !== null ? String(contractVal) : '')
66
+ );
67
+ });
68
+ }
69
+
70
+ onInput(event: Event) {
71
+ const target = event.target as HTMLInputElement | null;
72
+ if (!target) return;
73
+
74
+ const rawValue = target.value;
75
+ const sanitizedValue = this.sanitizer.sanitize(SecurityContext.HTML, rawValue) || '';
76
+
77
+ if (target.value !== sanitizedValue) {
78
+ target.value = sanitizedValue;
79
+ }
80
+
81
+ this.value.set(sanitizedValue);
82
+ this.experienceAdapter.updateState(this.contract().id, 'value', sanitizedValue);
83
+ }
84
+ }
@@ -0,0 +1,2 @@
1
+ <ng-container #vc></ng-container>
2
+ <slot></slot>
@@ -0,0 +1,19 @@
1
+ import { test, expect } from '@playwright/experimental-ct-angular';
2
+ import { VBoxComponent } from './vbox.component';
3
+ import AxeBuilder from '@axe-core/playwright';
4
+
5
+ test.describe('VBoxComponent Accessibility', () => {
6
+ test('should not have any automatically detectable accessibility issues', async ({
7
+ mount,
8
+ page,
9
+ }) => {
10
+ await mount(VBoxComponent, {
11
+ props: {
12
+ contract: { id: '1', type: 'vbox', props: { gap: '10px' } } as never,
13
+ },
14
+ });
15
+
16
+ const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
17
+ expect(accessibilityScanResults.violations).toEqual([]);
18
+ });
19
+ });
@@ -0,0 +1,5 @@
1
+ :host {
2
+ display: flex;
3
+ flex-direction: column;
4
+ box-sizing: border-box;
5
+ }
@@ -0,0 +1,74 @@
1
+ import {
2
+ Component,
3
+ input,
4
+ viewChild,
5
+ ViewContainerRef,
6
+ ChangeDetectionStrategy,
7
+ computed,
8
+ ViewEncapsulation,
9
+ } from '@angular/core';
10
+ import { InteractionContract } from '@origo/core';
11
+ import { OrigoAdapter, ContainerComponent } from '../../../adapters/web/adapter';
12
+
13
+ export interface VBoxProps {
14
+ gap?: number | string;
15
+ alignment?: 'start' | 'center' | 'end' | 'stretch';
16
+ padding?: number | string;
17
+ }
18
+
19
+ @Component({
20
+ selector: 'origo-vbox',
21
+ standalone: true,
22
+ templateUrl: './vbox.component.html',
23
+ styleUrls: ['./vbox.component.scss'],
24
+ changeDetection: ChangeDetectionStrategy.OnPush,
25
+ encapsulation: ViewEncapsulation.ShadowDom,
26
+ host: {
27
+ '[class.origo-vbox]': 'true',
28
+ '[style.gap]': 'computedGap()',
29
+ '[style.align-items]': 'computedAlignment()',
30
+ '[style.padding]': 'computedPadding()',
31
+ },
32
+ })
33
+ export class VBoxComponent implements OrigoAdapter<VBoxProps>, ContainerComponent {
34
+ static readonly contractSchema = {
35
+ gap: 'string',
36
+ alignment: 'string',
37
+ padding: 'string',
38
+ };
39
+ static readonly strictContract = false;
40
+
41
+ contract = input.required<InteractionContract<VBoxProps>>();
42
+
43
+ vc = viewChild.required('vc', { read: ViewContainerRef });
44
+
45
+ computedGap = computed(() => {
46
+ const gap = this.contract().props?.gap;
47
+ if (gap === undefined || gap === null || gap === '') return undefined;
48
+ const num = Number(gap);
49
+ return !isNaN(num) ? `${num}px` : String(gap);
50
+ });
51
+
52
+ computedAlignment = computed(() => {
53
+ const align = this.contract().props?.alignment;
54
+ switch (align) {
55
+ case 'start':
56
+ return 'flex-start';
57
+ case 'end':
58
+ return 'flex-end';
59
+ case 'center':
60
+ return 'center';
61
+ case 'stretch':
62
+ return 'stretch';
63
+ default:
64
+ return 'stretch';
65
+ }
66
+ });
67
+
68
+ computedPadding = computed(() => {
69
+ const padding = this.contract().props?.padding;
70
+ if (padding === undefined || padding === null || padding === '') return undefined;
71
+ const num = Number(padding);
72
+ return !isNaN(num) ? `${num}px` : String(padding);
73
+ });
74
+ }
@@ -0,0 +1,7 @@
1
+ export * from './lib/theme.provider';
2
+ export * from './lib/renderer.component';
3
+ export * from './lib/renderer.tokens';
4
+ export * from './adapters/web/adapter';
5
+ export * from './components/primitives/vbox/vbox.component';
6
+ export * from './components/primitives/text-input/text-input.component';
7
+ export * from './components/primitives/button/button.component';
@@ -0,0 +1,118 @@
1
+ import {
2
+ Component,
3
+ input,
4
+ inject,
5
+ ViewContainerRef,
6
+ effect,
7
+ viewChild,
8
+ DestroyRef,
9
+ } from '@angular/core';
10
+ import { ASTNode } from '@origo/core';
11
+ import { RENDERER_REGISTRY } from './renderer.tokens';
12
+ import { AdapterPipelineService, ContainerComponent } from '../adapters/web/adapter';
13
+
14
+ @Component({
15
+ selector: 'origo-renderer',
16
+ standalone: true,
17
+ template: `<ng-container #vc></ng-container>`,
18
+ })
19
+ export class OrigoRendererComponent {
20
+ node = input.required<ASTNode>();
21
+ private registry = inject(RENDERER_REGISTRY, { optional: true }) ?? new Map();
22
+ private vc = viewChild.required('vc', { read: ViewContainerRef });
23
+ private destroyRef = inject(DestroyRef);
24
+ private adapter = inject(AdapterPipelineService);
25
+ private renderId = 0;
26
+ private isDestroyed = false;
27
+
28
+ constructor() {
29
+ this.destroyRef.onDestroy(() => (this.isDestroyed = true));
30
+ effect(() => {
31
+ const n = this.node();
32
+ const container = this.vc();
33
+ const currentId = ++this.renderId;
34
+ container.clear();
35
+ if (n) {
36
+ this.renderTree(n, container, currentId);
37
+ }
38
+ });
39
+ }
40
+
41
+ private async renderTree(rootNode: ASTNode, rootVc: ViewContainerRef, traversalId: number) {
42
+ const queue: { node: ASTNode; vc: ViewContainerRef }[] = [{ node: rootNode, vc: rootVc }];
43
+ const CHUNK_SIZE = 50;
44
+ const visited = new Set<string>();
45
+
46
+ while (queue.length > 0) {
47
+ if (this.isDestroyed || traversalId !== this.renderId) return;
48
+ let processed = 0;
49
+
50
+ while (queue.length > 0 && processed < CHUNK_SIZE) {
51
+ const item = queue.shift();
52
+ if (!item) continue;
53
+ processed++;
54
+ const { node, vc } = item;
55
+
56
+ if (visited.has(node.id)) continue;
57
+ visited.add(node.id);
58
+
59
+ const componentType = this.registry.get(node.type);
60
+
61
+ if (!componentType) {
62
+ console.warn(`No primitive found for node type: ${node.type}`);
63
+ if (node.children && node.children.length > 0) {
64
+ for (const child of node.children) {
65
+ if (child) queue.push({ node: child, vc });
66
+ }
67
+ }
68
+ continue;
69
+ }
70
+
71
+ const componentRef = vc.createComponent(componentType);
72
+
73
+ // Extract optional schema/strict mode if defined on the component class
74
+ const schema = (componentType as unknown as Record<string, unknown>)['contractSchema'] as
75
+ | Record<string, 'string' | 'number' | 'boolean' | 'array' | 'object'>
76
+ | undefined;
77
+ const strict =
78
+ (componentType as unknown as Record<string, unknown>)['strictContract'] === true;
79
+
80
+ const preparedNode = this.adapter.prepareNode(node, schema, strict);
81
+
82
+ if ('contract' in (componentRef.instance as Record<string, unknown>)) {
83
+ componentRef.setInput('contract', preparedNode);
84
+ } else {
85
+ console.warn(
86
+ `Component for ${node.type} does not implement OrigoAdapter (missing 'contract' input).`
87
+ );
88
+ }
89
+
90
+ if (node.children && node.children.length > 0) {
91
+ componentRef.changeDetectorRef.detectChanges();
92
+ const instance = componentRef.instance as ContainerComponent;
93
+ let childVc = instance.vc || instance.viewContainerRef;
94
+
95
+ if (typeof childVc === 'function') {
96
+ childVc = childVc();
97
+ }
98
+
99
+ if (childVc) {
100
+ for (const child of node.children) {
101
+ if (child) queue.push({ node: child, vc: childVc });
102
+ }
103
+ } else {
104
+ console.warn(
105
+ `Component for ${node.type} has children but does not expose a ViewContainerRef as 'vc'`
106
+ );
107
+ }
108
+ } else {
109
+ componentRef.changeDetectorRef.markForCheck();
110
+ }
111
+ }
112
+
113
+ if (queue.length > 0) {
114
+ await new Promise(resolve => setTimeout(resolve, 0));
115
+ }
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,9 @@
1
+ import { InjectionToken, Type } from '@angular/core';
2
+
3
+ export const RENDERER_REGISTRY = new InjectionToken<Map<string, Type<unknown>>>(
4
+ 'RENDERER_REGISTRY',
5
+ {
6
+ providedIn: 'root',
7
+ factory: () => new Map(),
8
+ }
9
+ );
@@ -0,0 +1,26 @@
1
+ import { Type } from '@angular/core';
2
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
3
+
4
+ export interface RenderPrimitiveOptions<T> {
5
+ component: Type<T>;
6
+ inputs?: Partial<T>;
7
+ }
8
+
9
+ export async function renderPrimitive<T>(
10
+ options: RenderPrimitiveOptions<T>
11
+ ): Promise<ComponentFixture<T>> {
12
+ await TestBed.configureTestingModule({
13
+ imports: [options.component],
14
+ }).compileComponents();
15
+
16
+ const fixture = TestBed.createComponent(options.component);
17
+
18
+ if (options.inputs) {
19
+ for (const [key, value] of Object.entries(options.inputs)) {
20
+ fixture.componentRef.setInput(key, value);
21
+ }
22
+ }
23
+
24
+ fixture.detectChanges();
25
+ return fixture;
26
+ }
@@ -0,0 +1,83 @@
1
+ import { APP_INITIALIZER, Provider, PLATFORM_ID, Optional } from '@angular/core';
2
+ import { isPlatformBrowser, DOCUMENT } from '@angular/common';
3
+
4
+ import { loadAndInjectTheme, injectTheme } from '@origo/design-tokens/runtime';
5
+
6
+ export interface OrigoThemeOptions {
7
+ url?: string;
8
+ theme?: Record<string, unknown>;
9
+ targetElement?: HTMLElement | string;
10
+ }
11
+
12
+ export function themeInitializerFactory(
13
+ optionsOrUrl: string | OrigoThemeOptions,
14
+ platformId: object,
15
+ document: Document | null
16
+ ) {
17
+ return () => {
18
+ if (isPlatformBrowser(platformId)) {
19
+ const isString = typeof optionsOrUrl === 'string';
20
+ const url = isString ? optionsOrUrl : optionsOrUrl.url;
21
+ const theme = isString ? undefined : optionsOrUrl.theme;
22
+ // Resolve target element
23
+ let target: HTMLElement | undefined;
24
+ if (!isString && typeof optionsOrUrl.targetElement === 'string') {
25
+ target = document?.querySelector(optionsOrUrl.targetElement) as HTMLElement | undefined;
26
+ if (!target) {
27
+ console.warn(
28
+ `[origo-design] targetElement selector '${optionsOrUrl.targetElement}' matched nothing. Falling back to documentElement.`
29
+ );
30
+ target = document ? document.documentElement : undefined;
31
+ }
32
+ } else if (!isString && optionsOrUrl.targetElement instanceof HTMLElement) {
33
+ target = optionsOrUrl.targetElement;
34
+ } else {
35
+ target = document ? document.documentElement : undefined;
36
+ }
37
+
38
+ if (theme && url) {
39
+ const staticTeardown = injectTheme(theme, target);
40
+ return loadAndInjectTheme(url, target)
41
+ .then(fetchTeardown => {
42
+ return () => {
43
+ if (typeof staticTeardown === 'function') staticTeardown();
44
+ fetchTeardown();
45
+ };
46
+ })
47
+ .catch(e => {
48
+ console.error('[origo-design] theme init failed', e);
49
+ return typeof staticTeardown === 'function' ? staticTeardown : () => undefined;
50
+ });
51
+ } else if (theme) {
52
+ const teardown = injectTheme(theme, target);
53
+ return Promise.resolve(typeof teardown === 'function' ? teardown : () => undefined);
54
+ } else if (url) {
55
+ return loadAndInjectTheme(url, target).catch(e => {
56
+ console.error('[origo-design] theme init failed', e);
57
+ return () => undefined;
58
+ });
59
+ } else {
60
+ console.warn('[origo-design] options provided with neither theme nor url');
61
+ return Promise.resolve(() => undefined);
62
+ }
63
+ }
64
+ return Promise.resolve();
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Provides the Origo Design theme initialization for the Angular Web Adapter.
70
+ *
71
+ * @param optionsOrUrl URL string to the theme.json file, or an options object configuring the theme.
72
+ */
73
+ export function provideOrigoTheme(optionsOrUrl: string | OrigoThemeOptions): Provider[] {
74
+ return [
75
+ {
76
+ provide: APP_INITIALIZER,
77
+ useFactory: (platformId: object, document: Document | null) =>
78
+ themeInitializerFactory(optionsOrUrl, platformId, document),
79
+ deps: [PLATFORM_ID, [new Optional(), DOCUMENT]],
80
+ multi: true,
81
+ },
82
+ ];
83
+ }
@@ -0,0 +1,8 @@
1
+ import '@angular/compiler';
2
+ import { getTestBed } from '@angular/core/testing';
3
+ import {
4
+ BrowserDynamicTestingModule,
5
+ platformBrowserDynamicTesting,
6
+ } from '@angular/platform-browser-dynamic/testing';
7
+
8
+ getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
@@ -0,0 +1,92 @@
1
+ ## 0.0.22 (2026-09-04)
2
+
3
+ ### 🚀 Features
4
+
5
+ - scaffold playground application and e2e testing suite with initial editor component and schema registry ([f097894](https://github.com/OrigoStudio/origo-design/commit/f097894))
6
+ - add GitHub Actions workflow to automate library releases using Nx Release ([3d6651f](https://github.com/OrigoStudio/origo-design/commit/3d6651f))
7
+
8
+ ### ❤️ Thank You
9
+
10
+ - Hardik Patel @hardikpatelse
11
+
12
+ ## 0.0.21 (2026-09-02)
13
+
14
+ ### 🚀 Features
15
+
16
+ - add centralized test registry tool with validation logic, CI integration, and documentation artifacts ([384c1c7](https://github.com/OrigoStudio/origo-design/commit/384c1c7))
17
+ - implement central test registry with validation tools and CI integration ([02cf656](https://github.com/OrigoStudio/origo-design/commit/02cf656))
18
+ - initialize negative testing story and update sprint status tracking ([bbe5fdc](https://github.com/OrigoStudio/origo-design/commit/bbe5fdc))
19
+ - implement CLI framework with validation, testing, and CI/CD workflows ([ff6ce55](https://github.com/OrigoStudio/origo-design/commit/ff6ce55))
20
+ - implement validate command and schema validation logic for BADL files ([4f9e41d](https://github.com/OrigoStudio/origo-design/commit/4f9e41d))
21
+ - implement path traversal guards, remove process termination from library logic, and enable error cause preservation in @origo/cli ([0854f78](https://github.com/OrigoStudio/origo-design/commit/0854f78))
22
+ - initialize web-based editor component and track security remediation tasks in implementation artifacts ([65716bd](https://github.com/OrigoStudio/origo-design/commit/65716bd))
23
+ - document e2e npm verification plan for cli package releases ([22dab4c](https://github.com/OrigoStudio/origo-design/commit/22dab4c))
24
+ - initialize sprint status tracking and retrospective verification story for npm packaging ([c326751](https://github.com/OrigoStudio/origo-design/commit/c326751))
25
+ - add generated bundle for epic7-worker-csp spike ([16ca492](https://github.com/OrigoStudio/origo-design/commit/16ca492))
26
+ - add web worker CSP spike and associated implementation documentation ([ebf5aa4](https://github.com/OrigoStudio/origo-design/commit/ebf5aa4))
27
+ - implement web worker CSP spike with supporting documentation and harness tools ([f57b1e5](https://github.com/OrigoStudio/origo-design/commit/f57b1e5))
28
+ - add technical spike story for Epic 7 web worker CSP validation and ADR planning ([c977487](https://github.com/OrigoStudio/origo-design/commit/c977487))
29
+ - initialize sprint status tracking and create Epic 7 discovery research story ([b37b967](https://github.com/OrigoStudio/origo-design/commit/b37b967))
30
+ - implement entity generator command with template customization and ejection support ([02a3193](https://github.com/OrigoStudio/origo-design/commit/02a3193))
31
+ - implement entity generator command and template ejection logic in CLI ([d7d519a](https://github.com/OrigoStudio/origo-design/commit/d7d519a))
32
+ - implementation of 6-2-local-schema-validation ([45e67f6](https://github.com/OrigoStudio/origo-design/commit/45e67f6))
33
+
34
+ ### ❤️ Thank You
35
+
36
+ - Hardik Patel @hardikpatelse
37
+
38
+ ## 0.0.20 (2026-08-25)
39
+
40
+ ### 🚀 Features
41
+
42
+ - implementation of 6-1-cli-initialization-and-scaffolding ([f6aca9e](https://github.com/OrigoStudio/origo-design/commit/f6aca9e))
43
+
44
+ ### ❤️ Thank You
45
+
46
+ - Hardik Patel @hardikpatelse
47
+
48
+ ## 0.0.19 (2026-08-23)
49
+
50
+ ### 🚀 Features
51
+
52
+ - 5.5-5-define-secure-by-default-boilerplate-templates ([0eb6dde](https://github.com/OrigoStudio/origo-design/commit/0eb6dde))
53
+
54
+ ### ❤️ Thank You
55
+
56
+ - Hardik Patel @hardikpatelse
57
+
58
+ ## 0.0.18 (2026-08-23)
59
+
60
+ ### 🚀 Features
61
+
62
+ - 5.5-4-verify-core-compiler-source-map-preservation ([a3b0d9b](https://github.com/OrigoStudio/origo-design/commit/a3b0d9b))
63
+
64
+ ### 🩹 Fixes
65
+
66
+ - pipeline issue ([c1bbe47](https://github.com/OrigoStudio/origo-design/commit/c1bbe47))
67
+
68
+ ### ❤️ Thank You
69
+
70
+ - Hardik Patel @hardikpatelse
71
+
72
+ ## 0.0.17 (2026-08-21)
73
+
74
+ ### 🚀 Features
75
+
76
+ - 5.5-3-resolve-recursive-schema-resolution-tech-debt ([943e2e7](https://github.com/OrigoStudio/origo-design/commit/943e2e7))
77
+ - 5.5-2-establish-end-to-end-qa-protocols ([d8990ae](https://github.com/OrigoStudio/origo-design/commit/d8990ae))
78
+ - 5.5-2-establish-end-to-end-qa-protocols ([a31d97a](https://github.com/OrigoStudio/origo-design/commit/a31d97a))
79
+
80
+ ### ❤️ Thank You
81
+
82
+ - Hardik Patel @hardikpatelse
83
+
84
+ ## 0.0.16 (2026-08-21)
85
+
86
+ ### 🚀 Features
87
+
88
+ - 5.5-1-create-usability-quickstart-guide ([e740a1a](https://github.com/OrigoStudio/origo-design/commit/e740a1a))
89
+
90
+ ### ❤️ Thank You
91
+
92
+ - Hardik Patel @hardikpatelse
@@ -0,0 +1,11 @@
1
+ # cli
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build cli` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test cli` to execute the unit tests via [Jest](https://jestjs.io).
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@origo/cli",
3
+ "version": "0.0.22",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "main": "./src/index.js",
7
+ "types": "./src/index.d.ts",
8
+ "bin": {
9
+ "origo": "./src/main.js"
10
+ },
11
+ "dependencies": {
12
+ "@origo/core": "0.0.22",
13
+ "commander": "^15.0.0",
14
+ "tslib": "^2.3.0"
15
+ }
16
+ }
@@ -0,0 +1,20 @@
1
+ import { Command } from 'commander';
2
+ import { generateEntity } from '../../lib/generator';
3
+ import { handleError } from '../../utils/errors';
4
+
5
+ export function entityCommand(): Command {
6
+ const cmd = new Command('entity');
7
+ cmd
8
+ .description('Generate a new entity BADL schema')
9
+ .argument('<name>', 'Entity name (PascalCase, e.g. User)')
10
+ .option('--force', 'Overwrite existing file if it exists')
11
+ .option('--json', 'Output machine-readable JSON format')
12
+ .action(async (name: string, options: { force?: boolean; json?: boolean }) => {
13
+ try {
14
+ await generateEntity(name, { force: options.force, json: options.json });
15
+ } catch (error: unknown) {
16
+ handleError(error, { json: options.json });
17
+ }
18
+ });
19
+ return cmd;
20
+ }