@forumone/throughline-components 0.0.1 → 0.2.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +117 -28
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest-source.d.ts +17 -0
- package/dist/manifest-source.d.ts.map +1 -0
- package/dist/manifest-source.js +60 -0
- package/dist/manifest-source.js.map +1 -0
- package/dist/matching/index.d.ts +3 -0
- package/dist/matching/index.d.ts.map +1 -0
- package/dist/matching/index.js +2 -0
- package/dist/matching/index.js.map +1 -0
- package/dist/matching/tfidf.d.ts +13 -0
- package/dist/matching/tfidf.d.ts.map +1 -0
- package/dist/matching/tfidf.js +118 -0
- package/dist/matching/tfidf.js.map +1 -0
- package/dist/matching/types.d.ts +22 -0
- package/dist/matching/types.d.ts.map +1 -0
- package/dist/matching/types.js +2 -0
- package/dist/matching/types.js.map +1 -0
- package/dist/options.d.ts +125 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +42 -0
- package/dist/options.js.map +1 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +83 -0
- package/dist/plugin.js.map +1 -0
- package/dist/tools/find-anti-pattern.d.ts +9 -0
- package/dist/tools/find-anti-pattern.d.ts.map +1 -0
- package/dist/tools/find-anti-pattern.js +37 -0
- package/dist/tools/find-anti-pattern.js.map +1 -0
- package/dist/tools/get-contract.d.ts +4 -0
- package/dist/tools/get-contract.d.ts.map +1 -0
- package/dist/tools/get-contract.js +19 -0
- package/dist/tools/get-contract.js.map +1 -0
- package/dist/tools/get-tokens.d.ts +4 -0
- package/dist/tools/get-tokens.d.ts.map +1 -0
- package/dist/tools/get-tokens.js +19 -0
- package/dist/tools/get-tokens.js.map +1 -0
- package/dist/tools/get-variants.d.ts +4 -0
- package/dist/tools/get-variants.d.ts.map +1 -0
- package/dist/tools/get-variants.js +19 -0
- package/dist/tools/get-variants.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-components.d.ts +4 -0
- package/dist/tools/list-components.d.ts.map +1 -0
- package/dist/tools/list-components.js +25 -0
- package/dist/tools/list-components.js.map +1 -0
- package/dist/tools/suggest-for-intent.d.ts +12 -0
- package/dist/tools/suggest-for-intent.d.ts.map +1 -0
- package/dist/tools/suggest-for-intent.js +86 -0
- package/dist/tools/suggest-for-intent.js.map +1 -0
- package/dist/tools/validate-composition.d.ts +9 -0
- package/dist/tools/validate-composition.d.ts.map +1 -0
- package/dist/tools/validate-composition.js +37 -0
- package/dist/tools/validate-composition.js.map +1 -0
- package/dist/validation/composition.d.ts +38 -0
- package/dist/validation/composition.d.ts.map +1 -0
- package/dist/validation/composition.js +135 -0
- package/dist/validation/composition.js.map +1 -0
- package/package.json +62 -7
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @forumone/throughline-components
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#16](https://github.com/forumone/throughline/pull/16) [`4db5168`](https://github.com/forumone/throughline/commit/4db5168cfe83922ad371b7927029c21b009b1e53) Thanks [@briangraves](https://github.com/briangraves)! - Initial release. MCP server that exposes a design system manifest as seven conversational tools: `list_components`, `get_contract`, `get_variants`, `get_tokens`, `suggest_for_intent`, `validate_composition`, `find_anti_pattern`. Ships TF-IDF intent matching (no external deps); embeddings strategy reserved for a follow-up release. Accepts manifests as imported objects, remote URLs (with `refreshInterval`), or Payload collections.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Forum One Communications Corporation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,45 +1,134 @@
|
|
|
1
1
|
# @forumone/throughline-components
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MCP server that exposes a design system manifest as conversational primitives. Drop it into a Payload + Throughline app and Claude can list components, read contracts, suggest components for an intent, validate compositions, and surface anti-patterns — against any design system that satisfies the Throughline contract.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## What this package provides
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Seven MCP tools served at `${routePrefix}/mcp` (default `/api/components/mcp`):
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
| Tool | Purpose |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `list_components` | Discover what components exist (optionally filtered by category) |
|
|
12
|
+
| `get_contract` | Full `ComponentContract` for a named component |
|
|
13
|
+
| `get_variants` | Available variants and when to use each |
|
|
14
|
+
| `get_tokens` | Tokens a component consumes + configurable token-backed props |
|
|
15
|
+
| `suggest_for_intent` | Ranked component recommendations for a natural-language intent |
|
|
16
|
+
| `validate_composition` | Errors + warnings for a proposed block list against the design system's rules |
|
|
17
|
+
| `find_anti_pattern` | Surfaces structural anti-patterns (multiple Heroes, Hero at the bottom, etc.) |
|
|
10
18
|
|
|
11
|
-
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@forumone/throughline-components`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
19
|
+
Every consequential call writes to the audit log via `@forumone/throughline-core`'s audit writer.
|
|
15
20
|
|
|
16
|
-
##
|
|
21
|
+
## Installation
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add @forumone/throughline-components
|
|
25
|
+
```
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
Peer: `payload@^3.0.0`. Required runtime: `@forumone/throughline-core` (the components plugin asserts the `audit-log` capability at init).
|
|
21
28
|
|
|
22
|
-
|
|
29
|
+
## Usage
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
```ts
|
|
32
|
+
import { buildConfig } from 'payload'
|
|
33
|
+
import { auditPlugin, createInngestClient } from '@forumone/throughline-core'
|
|
34
|
+
import { componentsPlugin } from '@forumone/throughline-components'
|
|
35
|
+
import manifest from '@my-company/design-system/manifest' with { type: 'json' }
|
|
28
36
|
|
|
29
|
-
|
|
37
|
+
const inngest = createInngestClient({ id: 'my-site' })
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
export default buildConfig({
|
|
40
|
+
// collections, db, secret...
|
|
41
|
+
plugins: [
|
|
42
|
+
auditPlugin({ inngest }), // must come first (componentsPlugin requires audit-log)
|
|
43
|
+
componentsPlugin({
|
|
44
|
+
manifest: { type: 'object', manifest },
|
|
45
|
+
matching: { strategy: 'tfidf' },
|
|
46
|
+
}),
|
|
47
|
+
],
|
|
48
|
+
})
|
|
49
|
+
```
|
|
36
50
|
|
|
37
|
-
##
|
|
51
|
+
## Manifest sources
|
|
38
52
|
|
|
39
|
-
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
53
|
+
Three ways to supply the manifest:
|
|
42
54
|
|
|
43
|
-
|
|
55
|
+
**Imported object** — simplest; bundle the manifest with the app.
|
|
44
56
|
|
|
45
|
-
|
|
57
|
+
```ts
|
|
58
|
+
componentsPlugin({ manifest: { type: 'object', manifest: importedManifest } })
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Remote URL** — for design systems deployed independently.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
componentsPlugin({
|
|
65
|
+
manifest: {
|
|
66
|
+
type: 'url',
|
|
67
|
+
url: 'https://design-system.example.com/manifest.json',
|
|
68
|
+
refreshInterval: 3600, // seconds; manifest is re-fetched after the TTL expires
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Payload collection** — when the manifest is admin-editable.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
componentsPlugin({
|
|
77
|
+
manifest: { type: 'payload-collection', slug: 'design-system-manifest' },
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The loader reads the most recent document by `updatedAt`, optionally filtered by `documentId`, and looks for a `data` field on it before falling back to the document itself.
|
|
82
|
+
|
|
83
|
+
## Intent matching
|
|
84
|
+
|
|
85
|
+
The current shipping strategy is **TF-IDF**: zero external dependencies, weights `intent` more than `description`, fast enough that the first response after deploy is sub-100ms.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
componentsPlugin({
|
|
89
|
+
manifest: { /* ... */ },
|
|
90
|
+
matching: { strategy: 'tfidf', maxRecommendations: 5 },
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
An embeddings-based matcher is on the roadmap. The matcher interface is strategy-agnostic, so swapping it in won't change the tool surface. Until then, real recommendation quality depends on contract authoring — specifically, on how vividly each `intent` field describes the component's editorial purpose.
|
|
95
|
+
|
|
96
|
+
## Composition rules enforced
|
|
97
|
+
|
|
98
|
+
`validate_composition` checks:
|
|
99
|
+
|
|
100
|
+
- **`forbiddenAdjacent`** — neither the previous nor next block can be the forbidden type
|
|
101
|
+
- **`maxPerPage`** — total occurrences in the block list don't exceed the contract's `maxPerPage`
|
|
102
|
+
- **`requiredSiblings`** — warns (not errors) when a sibling that the contract expects isn't present
|
|
103
|
+
- **Unknown components** — flagged as an error
|
|
104
|
+
- **Unknown variants** — flagged as an error per block
|
|
105
|
+
|
|
106
|
+
`find_anti_pattern` surfaces:
|
|
107
|
+
|
|
108
|
+
- "Multiple X" labels when X appears more than once in the block list
|
|
109
|
+
- "End of page" / "bottom" labels when an X with `placement: ['page']` lands as the last block
|
|
110
|
+
|
|
111
|
+
The two tools are complementary. Use `validate_composition` to gate publishing and `find_anti_pattern` to surface design quality issues that aren't strictly invalid.
|
|
112
|
+
|
|
113
|
+
## The `_meta` parameter
|
|
114
|
+
|
|
115
|
+
`suggest_for_intent`, `validate_composition`, and `find_anti_pattern` all accept the framework's `_meta` payload (via `withMeta` from `@forumone/throughline-core`):
|
|
116
|
+
|
|
117
|
+
```jsonc
|
|
118
|
+
{
|
|
119
|
+
"intent": "introduce a new climate fellowship",
|
|
120
|
+
"_meta": {
|
|
121
|
+
"userPrompt": "I want a hero for the new climate program",
|
|
122
|
+
"reasoning": "The marketer asked for a page opener, not a section header",
|
|
123
|
+
"changesSummary": "Recommended Hero (split variant) for the program landing"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The audit writer reads these fields and stores them on every record so "why did Claude recommend Hero for the contact page?" is answerable later.
|
|
129
|
+
|
|
130
|
+
## Related packages
|
|
131
|
+
|
|
132
|
+
- `@forumone/throughline-design-contract` — the manifest schema this plugin reads
|
|
133
|
+
- `@forumone/throughline-reference-ds` — reference DS used as a test fixture and a starting point for clients
|
|
134
|
+
- `@forumone/throughline-core` — required peer; provides the audit log, MCP handler, and `withMeta` helper this plugin builds on
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,YAAY,EAAE,uBAAuB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
import { type LoadedManifest } from '@forumone/throughline-design-contract';
|
|
3
|
+
import type { ManifestSource } from './options.js';
|
|
4
|
+
export interface ManifestLoader {
|
|
5
|
+
/** Returns the cached manifest, refreshing if the URL TTL has expired. */
|
|
6
|
+
get(): Promise<LoadedManifest>;
|
|
7
|
+
/** Forces a re-fetch from the source. */
|
|
8
|
+
refresh(): Promise<LoadedManifest>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Builds a manifest loader from a source descriptor. The loader caches the
|
|
12
|
+
* parsed manifest indefinitely for `object` and `payload-collection` sources;
|
|
13
|
+
* for `url` sources it honors `refreshInterval` (seconds) and re-fetches when
|
|
14
|
+
* the cached copy is older than that.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createManifestLoader(source: ManifestSource, payload: Payload): ManifestLoader;
|
|
17
|
+
//# sourceMappingURL=manifest-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-source.d.ts","sourceRoot":"","sources":["../src/manifest-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAElD,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,GAAG,IAAI,OAAO,CAAC,cAAc,CAAC,CAAA;IAC9B,yCAAyC;IACzC,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,CAAA;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,GAAG,cAAc,CAuD7F"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { loadManifest, loadManifestFromUrl, } from '@forumone/throughline-design-contract';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a manifest loader from a source descriptor. The loader caches the
|
|
4
|
+
* parsed manifest indefinitely for `object` and `payload-collection` sources;
|
|
5
|
+
* for `url` sources it honors `refreshInterval` (seconds) and re-fetches when
|
|
6
|
+
* the cached copy is older than that.
|
|
7
|
+
*/
|
|
8
|
+
export function createManifestLoader(source, payload) {
|
|
9
|
+
let cached = null;
|
|
10
|
+
let lastLoadedAt = 0;
|
|
11
|
+
async function load() {
|
|
12
|
+
switch (source.type) {
|
|
13
|
+
case 'object':
|
|
14
|
+
return loadManifest(source.manifest);
|
|
15
|
+
case 'url':
|
|
16
|
+
return loadManifestFromUrl(source.url);
|
|
17
|
+
case 'payload-collection': {
|
|
18
|
+
const where = source.documentId
|
|
19
|
+
? { id: { equals: source.documentId } }
|
|
20
|
+
: undefined;
|
|
21
|
+
const result = await payload.find({
|
|
22
|
+
collection: source.slug,
|
|
23
|
+
...(where ? { where } : {}),
|
|
24
|
+
limit: 1,
|
|
25
|
+
sort: '-updatedAt',
|
|
26
|
+
});
|
|
27
|
+
const doc = result.docs[0];
|
|
28
|
+
if (!doc) {
|
|
29
|
+
const idClause = source.documentId ? ` with id "${source.documentId}"` : '';
|
|
30
|
+
throw new Error(`No manifest document found in collection "${source.slug}"${idClause}`);
|
|
31
|
+
}
|
|
32
|
+
const candidate = (doc['data'] ?? doc);
|
|
33
|
+
return loadManifest(candidate);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
async get() {
|
|
39
|
+
if (cached) {
|
|
40
|
+
if (source.type === 'url' && source.refreshInterval !== undefined) {
|
|
41
|
+
const ageMs = Date.now() - lastLoadedAt;
|
|
42
|
+
if (ageMs > source.refreshInterval * 1000) {
|
|
43
|
+
cached = await load();
|
|
44
|
+
lastLoadedAt = Date.now();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return cached;
|
|
48
|
+
}
|
|
49
|
+
cached = await load();
|
|
50
|
+
lastLoadedAt = Date.now();
|
|
51
|
+
return cached;
|
|
52
|
+
},
|
|
53
|
+
async refresh() {
|
|
54
|
+
cached = await load();
|
|
55
|
+
lastLoadedAt = Date.now();
|
|
56
|
+
return cached;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=manifest-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-source.js","sourceRoot":"","sources":["../src/manifest-source.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,YAAY,EACZ,mBAAmB,GACpB,MAAM,uCAAuC,CAAA;AAU9C;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAsB,EAAE,OAAgB;IAC3E,IAAI,MAAM,GAA0B,IAAI,CAAA;IACxC,IAAI,YAAY,GAAG,CAAC,CAAA;IAEpB,KAAK,UAAU,IAAI;QACjB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,QAAQ;gBACX,OAAO,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;YACtC,KAAK,KAAK;gBACR,OAAO,mBAAmB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACxC,KAAK,oBAAoB,CAAC,CAAC,CAAC;gBAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU;oBAC7B,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE;oBACvC,CAAC,CAAC,SAAS,CAAA;gBACb,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;oBAChC,UAAU,EAAE,MAAM,CAAC,IAAI;oBACvB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC3B,KAAK,EAAE,CAAC;oBACR,IAAI,EAAE,YAAY;iBACnB,CAAC,CAAA;gBACF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAwC,CAAA;gBACjE,IAAI,CAAC,GAAG,EAAE,CAAC;oBACT,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;oBAC3E,MAAM,IAAI,KAAK,CACb,6CAA6C,MAAM,CAAC,IAAI,IAAI,QAAQ,EAAE,CACvE,CAAA;gBACH,CAAC;gBACD,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAY,CAAA;gBACjD,OAAO,YAAY,CAAC,SAAS,CAAC,CAAA;YAChC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,CAAC,GAAG;YACP,IAAI,MAAM,EAAE,CAAC;gBACX,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;oBAClE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAA;oBACvC,IAAI,KAAK,GAAG,MAAM,CAAC,eAAe,GAAG,IAAI,EAAE,CAAC;wBAC1C,MAAM,GAAG,MAAM,IAAI,EAAE,CAAA;wBACrB,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;oBAC3B,CAAC;gBACH,CAAC;gBACD,OAAO,MAAM,CAAA;YACf,CAAC;YACD,MAAM,GAAG,MAAM,IAAI,EAAE,CAAA;YACrB,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACzB,OAAO,MAAM,CAAA;QACf,CAAC;QACD,KAAK,CAAC,OAAO;YACX,MAAM,GAAG,MAAM,IAAI,EAAE,CAAA;YACrB,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACzB,OAAO,MAAM,CAAA;QACf,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/matching/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/matching/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComponentContract } from '@forumone/throughline-design-contract';
|
|
2
|
+
import type { Matcher } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Simple TF-IDF matcher over component search documents. Indexed once at
|
|
5
|
+
* factory time; subsequent `rank()` calls scan the precomputed index.
|
|
6
|
+
*
|
|
7
|
+
* The search document weights `intent` more than `description`, and includes
|
|
8
|
+
* variant `whenToUse` text + example intents. Editorial language often shows
|
|
9
|
+
* up in those fields, so weighting them helps real intents like "introduce
|
|
10
|
+
* a new program" find the Hero's `intent` rather than its name.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createTfidfMatcher(components: ComponentContract[]): Matcher;
|
|
13
|
+
//# sourceMappingURL=tfidf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tfidf.d.ts","sourceRoot":"","sources":["../../src/matching/tfidf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAC9E,OAAO,KAAK,EAAE,OAAO,EAAmB,MAAM,YAAY,CAAA;AAE1D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,iBAAiB,EAAE,GAAG,OAAO,CA8C3E"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple TF-IDF matcher over component search documents. Indexed once at
|
|
3
|
+
* factory time; subsequent `rank()` calls scan the precomputed index.
|
|
4
|
+
*
|
|
5
|
+
* The search document weights `intent` more than `description`, and includes
|
|
6
|
+
* variant `whenToUse` text + example intents. Editorial language often shows
|
|
7
|
+
* up in those fields, so weighting them helps real intents like "introduce
|
|
8
|
+
* a new program" find the Hero's `intent` rather than its name.
|
|
9
|
+
*/
|
|
10
|
+
export function createTfidfMatcher(components) {
|
|
11
|
+
const index = components.map((component) => {
|
|
12
|
+
const document = makeSearchDocument(component);
|
|
13
|
+
const tokens = tokenize(document);
|
|
14
|
+
const termFreq = new Map();
|
|
15
|
+
for (const token of tokens) {
|
|
16
|
+
termFreq.set(token, (termFreq.get(token) ?? 0) + 1);
|
|
17
|
+
}
|
|
18
|
+
return { component, terms: termFreq, length: Math.max(tokens.length, 1) };
|
|
19
|
+
});
|
|
20
|
+
const docCount = index.length;
|
|
21
|
+
const docFreq = new Map();
|
|
22
|
+
for (const entry of index) {
|
|
23
|
+
for (const term of entry.terms.keys()) {
|
|
24
|
+
docFreq.set(term, (docFreq.get(term) ?? 0) + 1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const idf = new Map();
|
|
28
|
+
for (const [term, df] of docFreq) {
|
|
29
|
+
idf.set(term, Math.log((docCount + 1) / (df + 1)) + 1);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
rank(query) {
|
|
33
|
+
const queryCounts = new Map();
|
|
34
|
+
for (const token of tokenize(query)) {
|
|
35
|
+
queryCounts.set(token, (queryCounts.get(token) ?? 0) + 1);
|
|
36
|
+
}
|
|
37
|
+
const scored = index.map((entry) => {
|
|
38
|
+
let score = 0;
|
|
39
|
+
for (const [term, queryCount] of queryCounts) {
|
|
40
|
+
const docCountForTerm = entry.terms.get(term) ?? 0;
|
|
41
|
+
if (docCountForTerm === 0)
|
|
42
|
+
continue;
|
|
43
|
+
const tf = docCountForTerm / entry.length;
|
|
44
|
+
const termIdf = idf.get(term) ?? 0;
|
|
45
|
+
score += tf * termIdf * queryCount;
|
|
46
|
+
}
|
|
47
|
+
return { component: entry.component, score };
|
|
48
|
+
});
|
|
49
|
+
scored.sort((a, b) => b.score - a.score);
|
|
50
|
+
return scored;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function makeSearchDocument(component) {
|
|
55
|
+
const parts = [
|
|
56
|
+
// Intent and description are the most-weighted fields. Repeat the intent
|
|
57
|
+
// twice so it dominates rankings when its terms match.
|
|
58
|
+
component.intent,
|
|
59
|
+
component.intent,
|
|
60
|
+
component.description,
|
|
61
|
+
component.name,
|
|
62
|
+
...(component.content.variants ?? []).flatMap((v) => [v.name, v.whenToUse]),
|
|
63
|
+
...component.examples.map((e) => e.intent),
|
|
64
|
+
];
|
|
65
|
+
return parts.join(' ');
|
|
66
|
+
}
|
|
67
|
+
function tokenize(text) {
|
|
68
|
+
return text
|
|
69
|
+
.toLowerCase()
|
|
70
|
+
.replace(/[^\w\s]/g, ' ')
|
|
71
|
+
.split(/\s+/)
|
|
72
|
+
.filter((t) => t.length > 2 && !STOP_WORDS.has(t));
|
|
73
|
+
}
|
|
74
|
+
const STOP_WORDS = new Set([
|
|
75
|
+
'the',
|
|
76
|
+
'and',
|
|
77
|
+
'for',
|
|
78
|
+
'with',
|
|
79
|
+
'that',
|
|
80
|
+
'this',
|
|
81
|
+
'have',
|
|
82
|
+
'has',
|
|
83
|
+
'had',
|
|
84
|
+
'are',
|
|
85
|
+
'was',
|
|
86
|
+
'were',
|
|
87
|
+
'been',
|
|
88
|
+
'being',
|
|
89
|
+
'will',
|
|
90
|
+
'would',
|
|
91
|
+
'should',
|
|
92
|
+
'could',
|
|
93
|
+
'may',
|
|
94
|
+
'might',
|
|
95
|
+
'must',
|
|
96
|
+
'shall',
|
|
97
|
+
'can',
|
|
98
|
+
'these',
|
|
99
|
+
'those',
|
|
100
|
+
'its',
|
|
101
|
+
'use',
|
|
102
|
+
'used',
|
|
103
|
+
'using',
|
|
104
|
+
'when',
|
|
105
|
+
'where',
|
|
106
|
+
'how',
|
|
107
|
+
'what',
|
|
108
|
+
'which',
|
|
109
|
+
'into',
|
|
110
|
+
'onto',
|
|
111
|
+
'than',
|
|
112
|
+
'then',
|
|
113
|
+
'them',
|
|
114
|
+
'they',
|
|
115
|
+
'their',
|
|
116
|
+
'there',
|
|
117
|
+
]);
|
|
118
|
+
//# sourceMappingURL=tfidf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tfidf.js","sourceRoot":"","sources":["../../src/matching/tfidf.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ComponentContract } from '@forumone/throughline-design-contract';
|
|
2
|
+
export interface RankedComponent {
|
|
3
|
+
component: ComponentContract;
|
|
4
|
+
score: number;
|
|
5
|
+
}
|
|
6
|
+
export interface Matcher {
|
|
7
|
+
/** Returns the indexed components ranked best-first against the query. */
|
|
8
|
+
rank(query: string): RankedComponent[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Surface returned by {@link suggest_for_intent}. Same shape regardless of the
|
|
12
|
+
* underlying matcher.
|
|
13
|
+
*/
|
|
14
|
+
export interface RankedSuggestion {
|
|
15
|
+
component: string;
|
|
16
|
+
score: number;
|
|
17
|
+
reasoning: string;
|
|
18
|
+
matchedIntent: string;
|
|
19
|
+
variant?: string;
|
|
20
|
+
warnings?: string[];
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/matching/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAA;AAE9E,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,iBAAiB,CAAA;IAC5B,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,OAAO;IACtB,0EAA0E;IAC1E,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAAA;CACvC;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/matching/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { BaseCorePluginOptions } from '@forumone/throughline-plugin-contract';
|
|
3
|
+
import type { Manifest } from '@forumone/throughline-design-contract';
|
|
4
|
+
export type ManifestSource = {
|
|
5
|
+
type: 'object';
|
|
6
|
+
manifest: Manifest;
|
|
7
|
+
} | {
|
|
8
|
+
type: 'url';
|
|
9
|
+
url: string;
|
|
10
|
+
refreshInterval?: number;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'payload-collection';
|
|
13
|
+
slug: string;
|
|
14
|
+
documentId?: string;
|
|
15
|
+
};
|
|
16
|
+
export interface MatchingConfig {
|
|
17
|
+
/**
|
|
18
|
+
* Strategy for ranking components against a natural-language intent.
|
|
19
|
+
* `tfidf` ships now and requires no external dependencies; `embeddings`
|
|
20
|
+
* is reserved for a follow-up.
|
|
21
|
+
*/
|
|
22
|
+
strategy: 'tfidf';
|
|
23
|
+
/** Maximum number of recommendations from `suggest_for_intent`. Default: 5. */
|
|
24
|
+
maxRecommendations?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ComponentsPluginOptions extends BaseCorePluginOptions {
|
|
27
|
+
/** Required: where the design system manifest comes from. */
|
|
28
|
+
manifest: ManifestSource;
|
|
29
|
+
/** Optional: how the plugin matches intents to components. Defaults to TF-IDF. */
|
|
30
|
+
matching?: MatchingConfig;
|
|
31
|
+
}
|
|
32
|
+
export declare const ComponentsPluginOptionsSchema: z.ZodObject<{
|
|
33
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
34
|
+
routePrefix: z.ZodOptional<z.ZodString>;
|
|
35
|
+
manifest: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
36
|
+
type: z.ZodLiteral<"object">;
|
|
37
|
+
manifest: z.ZodUnknown;
|
|
38
|
+
}, "strip", z.ZodTypeAny, {
|
|
39
|
+
type: "object";
|
|
40
|
+
manifest?: unknown;
|
|
41
|
+
}, {
|
|
42
|
+
type: "object";
|
|
43
|
+
manifest?: unknown;
|
|
44
|
+
}>, z.ZodObject<{
|
|
45
|
+
type: z.ZodLiteral<"url">;
|
|
46
|
+
url: z.ZodString;
|
|
47
|
+
refreshInterval: z.ZodOptional<z.ZodNumber>;
|
|
48
|
+
}, "strip", z.ZodTypeAny, {
|
|
49
|
+
url: string;
|
|
50
|
+
type: "url";
|
|
51
|
+
refreshInterval?: number | undefined;
|
|
52
|
+
}, {
|
|
53
|
+
url: string;
|
|
54
|
+
type: "url";
|
|
55
|
+
refreshInterval?: number | undefined;
|
|
56
|
+
}>, z.ZodObject<{
|
|
57
|
+
type: z.ZodLiteral<"payload-collection">;
|
|
58
|
+
slug: z.ZodString;
|
|
59
|
+
documentId: z.ZodOptional<z.ZodString>;
|
|
60
|
+
}, "strip", z.ZodTypeAny, {
|
|
61
|
+
type: "payload-collection";
|
|
62
|
+
slug: string;
|
|
63
|
+
documentId?: string | undefined;
|
|
64
|
+
}, {
|
|
65
|
+
type: "payload-collection";
|
|
66
|
+
slug: string;
|
|
67
|
+
documentId?: string | undefined;
|
|
68
|
+
}>]>;
|
|
69
|
+
matching: z.ZodOptional<z.ZodObject<{
|
|
70
|
+
strategy: z.ZodEnum<["tfidf"]>;
|
|
71
|
+
maxRecommendations: z.ZodOptional<z.ZodNumber>;
|
|
72
|
+
}, "strip", z.ZodTypeAny, {
|
|
73
|
+
strategy: "tfidf";
|
|
74
|
+
maxRecommendations?: number | undefined;
|
|
75
|
+
}, {
|
|
76
|
+
strategy: "tfidf";
|
|
77
|
+
maxRecommendations?: number | undefined;
|
|
78
|
+
}>>;
|
|
79
|
+
}, "strip", z.ZodTypeAny, {
|
|
80
|
+
manifest: {
|
|
81
|
+
type: "object";
|
|
82
|
+
manifest?: unknown;
|
|
83
|
+
} | {
|
|
84
|
+
url: string;
|
|
85
|
+
type: "url";
|
|
86
|
+
refreshInterval?: number | undefined;
|
|
87
|
+
} | {
|
|
88
|
+
type: "payload-collection";
|
|
89
|
+
slug: string;
|
|
90
|
+
documentId?: string | undefined;
|
|
91
|
+
};
|
|
92
|
+
matching?: {
|
|
93
|
+
strategy: "tfidf";
|
|
94
|
+
maxRecommendations?: number | undefined;
|
|
95
|
+
} | undefined;
|
|
96
|
+
enabled?: boolean | undefined;
|
|
97
|
+
routePrefix?: string | undefined;
|
|
98
|
+
}, {
|
|
99
|
+
manifest: {
|
|
100
|
+
type: "object";
|
|
101
|
+
manifest?: unknown;
|
|
102
|
+
} | {
|
|
103
|
+
url: string;
|
|
104
|
+
type: "url";
|
|
105
|
+
refreshInterval?: number | undefined;
|
|
106
|
+
} | {
|
|
107
|
+
type: "payload-collection";
|
|
108
|
+
slug: string;
|
|
109
|
+
documentId?: string | undefined;
|
|
110
|
+
};
|
|
111
|
+
matching?: {
|
|
112
|
+
strategy: "tfidf";
|
|
113
|
+
maxRecommendations?: number | undefined;
|
|
114
|
+
} | undefined;
|
|
115
|
+
enabled?: boolean | undefined;
|
|
116
|
+
routePrefix?: string | undefined;
|
|
117
|
+
}>;
|
|
118
|
+
/**
|
|
119
|
+
* Validates plugin options at load time. Throws with a multi-line message
|
|
120
|
+
* on schema failure or with a targeted message on cross-field violations.
|
|
121
|
+
* Plugins that depend on this server must initialize after `componentsPlugin`
|
|
122
|
+
* because the registry capability check happens during onInit.
|
|
123
|
+
*/
|
|
124
|
+
export declare function validateOptions(options: ComponentsPluginOptions): ComponentsPluginOptions;
|
|
125
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAA;AAClF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAA;AAErE,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAErE,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,+EAA+E;IAC/E,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,uBAAwB,SAAQ,qBAAqB;IACpE,6DAA6D;IAC7D,QAAQ,EAAE,cAAc,CAAA;IACxB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B;AAgBD,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUxC,CAAA;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,uBAAuB,GAAG,uBAAuB,CASzF"}
|