@usefidel/contracts 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/onboarding-config.d.ts +78 -0
- package/dist/onboarding-config.js +113 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -31,6 +31,19 @@ import { RUN_ERROR_CODES, resolveRunDisplay } from '@usefidel/contracts';
|
|
|
31
31
|
import { resolveRunDisplay } from '@usefidel/contracts/run-errors';
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
The canonical `fidel.config.json` builders live behind their own entry point, so
|
|
35
|
+
importing them is a deliberate act rather than a side effect of importing the
|
|
36
|
+
package:
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import { buildDsConfigJson, nameFromUrl } from '@usefidel/contracts/onboarding-config';
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
These are the ONE implementation of what `fidel init` writes. The CLI uses them
|
|
43
|
+
to write the file and the onboarding UI uses them to render the example a user
|
|
44
|
+
copies, so the two cannot drift. Their output formatting — two-space indent, key
|
|
45
|
+
order, trailing newline — is part of the contract and is asserted byte for byte.
|
|
46
|
+
|
|
34
47
|
Ships ESM with TypeScript declarations. No runtime dependencies.
|
|
35
48
|
|
|
36
49
|
## Versioning
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical `fidel.config.json` builders — the ONE implementation.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS LIVES IN THE CONTRACTS PACKAGE (F-10)
|
|
5
|
+
* ----------------------------------------------
|
|
6
|
+
* Two surfaces have to agree, byte for byte, about what `fidel init` writes:
|
|
7
|
+
*
|
|
8
|
+
* 1. the CLI, which writes the file, and
|
|
9
|
+
* 2. the onboarding UI in `usefidel/fidel-web`, which SHOWS the user that
|
|
10
|
+
* exact text to copy.
|
|
11
|
+
*
|
|
12
|
+
* They used to agree by accident. The builders lived in
|
|
13
|
+
* `github-action/src/init.ts`, and a test in the webapp imported them across a
|
|
14
|
+
* relative path to assert the displayed example matched. The frontend split
|
|
15
|
+
* broke that path — `github-action/` stayed in the monorepo — and the test was
|
|
16
|
+
* excluded rather than deleted so the loss stayed visible (finding F-10). From
|
|
17
|
+
* then until this module existed, the CLI and the onboarding example could
|
|
18
|
+
* diverge silently, and the failure would land on a new user copying a config
|
|
19
|
+
* that no longer works.
|
|
20
|
+
*
|
|
21
|
+
* Publishing the builders makes the agreement structural instead of hopeful:
|
|
22
|
+
*
|
|
23
|
+
* onboarding-config (here) -> fidel init (writes the file)
|
|
24
|
+
* onboarding-config (here) -> onboarding UI (shows + asserts the file)
|
|
25
|
+
*
|
|
26
|
+
* NOT a shared-utils dumping ground. This module holds only the pure logic both
|
|
27
|
+
* consumers need to produce identical config bytes. It has no imports, touches
|
|
28
|
+
* no I/O, and knows nothing about prompts, filesystems, or the matching engine.
|
|
29
|
+
* Anything that does not have to be identical across both repos does not belong
|
|
30
|
+
* here — it belongs in the consumer that needs it.
|
|
31
|
+
*
|
|
32
|
+
* Exposed under its own entry point (`@usefidel/contracts/onboarding-config`)
|
|
33
|
+
* rather than the package root, so importing it is a deliberate act.
|
|
34
|
+
*
|
|
35
|
+
* FORMATTING IS PART OF THE CONTRACT. Users copy this text by hand. Two-space
|
|
36
|
+
* indent, key order, and the trailing newline are all load-bearing, and the
|
|
37
|
+
* webapp asserts them byte for byte. Changing any of them is a breaking change
|
|
38
|
+
* to what people paste into their repositories.
|
|
39
|
+
*/
|
|
40
|
+
/** One `designSystemChecks[]` entry. */
|
|
41
|
+
export interface DsSelection {
|
|
42
|
+
name: string;
|
|
43
|
+
url: string;
|
|
44
|
+
brandKey?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Derive a check name from a URL.
|
|
48
|
+
*
|
|
49
|
+
* Same convention as the Figma branch's frame naming: `hostname/path`, falling
|
|
50
|
+
* back to the bare hostname when the path is empty, and to the raw input when
|
|
51
|
+
* the URL will not parse (a name is better than a crash during setup).
|
|
52
|
+
*/
|
|
53
|
+
export declare function nameFromUrl(url: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Design-system branch: write or merge `designSystemChecks[]` alongside any
|
|
56
|
+
* existing `checks[]` (Figma) entries.
|
|
57
|
+
*
|
|
58
|
+
* Never touches `checks[]` — the two arrays are independent siblings per the
|
|
59
|
+
* `_shared/app-config.ts` contract, and the runner executes each leg
|
|
60
|
+
* separately. Entries are merged, deduplicating on the (name, url) pair.
|
|
61
|
+
*
|
|
62
|
+
* `checks` is emitted FIRST when present. That ordering is observable in the
|
|
63
|
+
* output the user copies, so it is fixed, not incidental.
|
|
64
|
+
*/
|
|
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";
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical `fidel.config.json` builders — the ONE implementation.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS LIVES IN THE CONTRACTS PACKAGE (F-10)
|
|
5
|
+
* ----------------------------------------------
|
|
6
|
+
* Two surfaces have to agree, byte for byte, about what `fidel init` writes:
|
|
7
|
+
*
|
|
8
|
+
* 1. the CLI, which writes the file, and
|
|
9
|
+
* 2. the onboarding UI in `usefidel/fidel-web`, which SHOWS the user that
|
|
10
|
+
* exact text to copy.
|
|
11
|
+
*
|
|
12
|
+
* They used to agree by accident. The builders lived in
|
|
13
|
+
* `github-action/src/init.ts`, and a test in the webapp imported them across a
|
|
14
|
+
* relative path to assert the displayed example matched. The frontend split
|
|
15
|
+
* broke that path — `github-action/` stayed in the monorepo — and the test was
|
|
16
|
+
* excluded rather than deleted so the loss stayed visible (finding F-10). From
|
|
17
|
+
* then until this module existed, the CLI and the onboarding example could
|
|
18
|
+
* diverge silently, and the failure would land on a new user copying a config
|
|
19
|
+
* that no longer works.
|
|
20
|
+
*
|
|
21
|
+
* Publishing the builders makes the agreement structural instead of hopeful:
|
|
22
|
+
*
|
|
23
|
+
* onboarding-config (here) -> fidel init (writes the file)
|
|
24
|
+
* onboarding-config (here) -> onboarding UI (shows + asserts the file)
|
|
25
|
+
*
|
|
26
|
+
* NOT a shared-utils dumping ground. This module holds only the pure logic both
|
|
27
|
+
* consumers need to produce identical config bytes. It has no imports, touches
|
|
28
|
+
* no I/O, and knows nothing about prompts, filesystems, or the matching engine.
|
|
29
|
+
* Anything that does not have to be identical across both repos does not belong
|
|
30
|
+
* here — it belongs in the consumer that needs it.
|
|
31
|
+
*
|
|
32
|
+
* Exposed under its own entry point (`@usefidel/contracts/onboarding-config`)
|
|
33
|
+
* rather than the package root, so importing it is a deliberate act.
|
|
34
|
+
*
|
|
35
|
+
* FORMATTING IS PART OF THE CONTRACT. Users copy this text by hand. Two-space
|
|
36
|
+
* indent, key order, and the trailing newline are all load-bearing, and the
|
|
37
|
+
* webapp asserts them byte for byte. Changing any of them is a breaking change
|
|
38
|
+
* to what people paste into their repositories.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* Derive a check name from a URL.
|
|
42
|
+
*
|
|
43
|
+
* Same convention as the Figma branch's frame naming: `hostname/path`, falling
|
|
44
|
+
* back to the bare hostname when the path is empty, and to the raw input when
|
|
45
|
+
* the URL will not parse (a name is better than a crash during setup).
|
|
46
|
+
*/
|
|
47
|
+
export function nameFromUrl(url) {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = new URL(url);
|
|
50
|
+
const path = parsed.pathname.replace(/^\/|\/$/g, "");
|
|
51
|
+
return path ? `${parsed.hostname}/${path}` : parsed.hostname;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return url;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Design-system branch: write or merge `designSystemChecks[]` alongside any
|
|
59
|
+
* existing `checks[]` (Figma) entries.
|
|
60
|
+
*
|
|
61
|
+
* Never touches `checks[]` — the two arrays are independent siblings per the
|
|
62
|
+
* `_shared/app-config.ts` contract, and the runner executes each leg
|
|
63
|
+
* separately. Entries are merged, deduplicating on the (name, url) pair.
|
|
64
|
+
*
|
|
65
|
+
* `checks` is emitted FIRST when present. That ordering is observable in the
|
|
66
|
+
* output the user copies, so it is fixed, not incidental.
|
|
67
|
+
*/
|
|
68
|
+
export function buildDsConfigJson(existingRaw, dsSelections) {
|
|
69
|
+
const existingChecks = existingRaw?.checks;
|
|
70
|
+
const rawDs = existingRaw?.designSystemChecks;
|
|
71
|
+
const existingDsChecks = Array.isArray(rawDs) ? rawDs : [];
|
|
72
|
+
const merged = [...existingDsChecks];
|
|
73
|
+
for (const sel of dsSelections) {
|
|
74
|
+
if (!merged.some((c) => c.name === sel.name && c.url === sel.url)) {
|
|
75
|
+
merged.push(sel);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const out = {};
|
|
79
|
+
if (existingChecks !== undefined) {
|
|
80
|
+
out.checks = existingChecks;
|
|
81
|
+
}
|
|
82
|
+
out.designSystemChecks = merged;
|
|
83
|
+
return JSON.stringify(out, null, 2) + "\n";
|
|
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
|
+
`;
|
package/package.json
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"The source of truth still lives in the monorepo at packages/contracts/."
|
|
8
8
|
],
|
|
9
9
|
"name": "@usefidel/contracts",
|
|
10
|
-
"version": "0.
|
|
11
|
-
"description": "Shared, code-free contracts between Fidel surfaces. Run-error taxonomy
|
|
10
|
+
"version": "0.5.0",
|
|
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,
|
|
14
14
|
"type": "module",
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"./theme-intake": {
|
|
28
28
|
"types": "./dist/theme-intake.d.ts",
|
|
29
29
|
"import": "./dist/theme-intake.js"
|
|
30
|
+
},
|
|
31
|
+
"./onboarding-config": {
|
|
32
|
+
"types": "./dist/onboarding-config.d.ts",
|
|
33
|
+
"import": "./dist/onboarding-config.js"
|
|
30
34
|
}
|
|
31
35
|
},
|
|
32
36
|
"files": [
|