@usefidel/contracts 0.4.0 → 0.6.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.
@@ -63,3 +63,39 @@ export declare function nameFromUrl(url: string): string;
63
63
  * output the user copies, so it is fixed, not incidental.
64
64
  */
65
65
  export declare function buildDsConfigJson(existingRaw: Record<string, unknown> | null, dsSelections: DsSelection[]): string;
66
+ /**
67
+ * The GitHub Actions workflow `fidel init` writes to `.github/workflows/`.
68
+ *
69
+ * Here for the same reason as the builders above: the onboarding UI shows this
70
+ * text for a user to copy, and `fidel init` writes it. If the two disagree, a
71
+ * user commits a workflow that does not match the one we told them to use.
72
+ *
73
+ * A plain constant rather than a builder because init writes it identically on
74
+ * both branches — the Figma and design-system flows share one workflow.
75
+ *
76
+ * The trailing newline is intentional and asserted; the file ends with one.
77
+ */
78
+ export declare const WORKFLOW_YAML = "name: Design Validation\non:\n pull_request:\n types: [opened, synchronize]\n\npermissions:\n contents: read\n pull-requests: write\n id-token: write\n\njobs:\n fidel:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: usefidel/fidel-action@v1\n";
79
+ /** One frame chosen in `fidel init`'s Figma picker. */
80
+ export interface Selection {
81
+ nodeId: string;
82
+ name: string;
83
+ page: string;
84
+ width: number;
85
+ height: number;
86
+ url: string;
87
+ }
88
+ /**
89
+ * Figma branch: write `checks[]`.
90
+ *
91
+ * `viewport` is emitted ONLY when the frame is not 1440x900, and only when the
92
+ * dimensions are real positive integers — a Selection missing width/height makes
93
+ * Math.round() return NaN, which serializes to null, and
94
+ * `viewport: {width: null, height: null}` is rejected outright by the
95
+ * marketplace Action's own validator. The CLI must never write a config our own
96
+ * Action refuses to load.
97
+ *
98
+ * Already-serialized checks from an existing fidel.config.json arrive on a
99
+ * re-run carrying `figma`/`url` and no `nodeId`; they pass straight through.
100
+ */
101
+ export declare function buildConfigJson(selections: Selection[], fileKey: string): string;
@@ -82,3 +82,81 @@ export function buildDsConfigJson(existingRaw, dsSelections) {
82
82
  out.designSystemChecks = merged;
83
83
  return JSON.stringify(out, null, 2) + "\n";
84
84
  }
85
+ /**
86
+ * The GitHub Actions workflow `fidel init` writes to `.github/workflows/`.
87
+ *
88
+ * Here for the same reason as the builders above: the onboarding UI shows this
89
+ * text for a user to copy, and `fidel init` writes it. If the two disagree, a
90
+ * user commits a workflow that does not match the one we told them to use.
91
+ *
92
+ * A plain constant rather than a builder because init writes it identically on
93
+ * both branches — the Figma and design-system flows share one workflow.
94
+ *
95
+ * The trailing newline is intentional and asserted; the file ends with one.
96
+ */
97
+ export const WORKFLOW_YAML = `name: Design Validation
98
+ on:
99
+ pull_request:
100
+ types: [opened, synchronize]
101
+
102
+ permissions:
103
+ contents: read
104
+ pull-requests: write
105
+ id-token: write
106
+
107
+ jobs:
108
+ fidel:
109
+ runs-on: ubuntu-latest
110
+ steps:
111
+ - uses: actions/checkout@v4
112
+ - uses: usefidel/fidel-action@v1
113
+ `;
114
+ /** `https://www.figma.com/design/<key>/?node-id=<a-b>` — Figma wants dashes, not colons. */
115
+ function buildFigmaFrameUrl(fileKey, nodeId) {
116
+ const dashNodeId = nodeId.replace(/:/g, '-');
117
+ return `https://www.figma.com/design/${fileKey}/?node-id=${dashNodeId}`;
118
+ }
119
+ /**
120
+ * Figma branch: write `checks[]`.
121
+ *
122
+ * `viewport` is emitted ONLY when the frame is not 1440x900, and only when the
123
+ * dimensions are real positive integers — a Selection missing width/height makes
124
+ * Math.round() return NaN, which serializes to null, and
125
+ * `viewport: {width: null, height: null}` is rejected outright by the
126
+ * marketplace Action's own validator. The CLI must never write a config our own
127
+ * Action refuses to load.
128
+ *
129
+ * Already-serialized checks from an existing fidel.config.json arrive on a
130
+ * re-run carrying `figma`/`url` and no `nodeId`; they pass straight through.
131
+ */
132
+ export function buildConfigJson(selections, fileKey) {
133
+ const checks = selections.map((s) => {
134
+ // Already-serialized checks from an existing fidel.config.json come through
135
+ // here on a re-run: they carry `figma`/`url` and have NO `nodeId`. Passing
136
+ // them to buildFigmaFrameUrl() would throw on `nodeId.replace`. Pass them
137
+ // straight through — they're already in output shape.
138
+ if (s.nodeId === undefined && s.figma !== undefined) {
139
+ return s;
140
+ }
141
+ const check = {
142
+ name: s.name,
143
+ figma: buildFigmaFrameUrl(fileKey, s.nodeId),
144
+ url: s.url,
145
+ };
146
+ // A Selection missing width/height makes Math.round() return NaN, which
147
+ // serializes to `null` — and `viewport: { width: null, height: null }` is
148
+ // rejected outright by the marketplace Action's own validator
149
+ // (config.ts requirePositiveInteger → "must be a positive integer").
150
+ // Our CLI must never write a config our Action refuses to load. When the
151
+ // dimensions aren't real positive integers, omit `viewport` entirely: an
152
+ // absent viewport means the 1440x900 default, which is the right fallback.
153
+ const w = Math.round(s.width);
154
+ const h = Math.round(s.height);
155
+ const hasRealDimensions = Number.isInteger(w) && w > 0 && Number.isInteger(h) && h > 0;
156
+ if (hasRealDimensions && (w !== 1440 || h !== 900)) {
157
+ check.viewport = { width: w, height: h };
158
+ }
159
+ return check;
160
+ });
161
+ return JSON.stringify({ checks }, null, 2) + '\n';
162
+ }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "The source of truth still lives in the monorepo at packages/contracts/."
8
8
  ],
9
9
  "name": "@usefidel/contracts",
10
- "version": "0.4.0",
10
+ "version": "0.6.0",
11
11
  "description": "Shared, code-free contracts between Fidel surfaces. Run-error taxonomy, theme-intake wire types, and the canonical fidel.config.json builders.",
12
12
  "license": "UNLICENSED",
13
13
  "private": false,