handoff-app 2.0.0-6 → 2.0.0-8

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 (233) hide show
  1. package/README.md +240 -52
  2. package/UPGRADE.md +218 -0
  3. package/config/config.template.js +1 -4
  4. package/config/config.template.ts +1 -2
  5. package/dist/app-builder/build.js +12 -6
  6. package/dist/app-builder/client-config.js +3 -1
  7. package/dist/app-builder/config-diff/strategies/pattern.js +3 -3
  8. package/dist/app-builder/watchers/component.js +5 -5
  9. package/dist/app-builder/watchers/utils.d.ts +2 -3
  10. package/dist/app-builder/watchers.d.ts +8 -15
  11. package/dist/app-builder/watchers.js +70 -61
  12. package/dist/cache/build-cache.js +23 -23
  13. package/dist/catalog/define.d.ts +6 -0
  14. package/dist/catalog/define.js +72 -0
  15. package/dist/catalog/implementation.d.ts +27 -0
  16. package/dist/catalog/implementation.js +133 -0
  17. package/dist/catalog/index.d.ts +6 -0
  18. package/dist/catalog/index.js +22 -0
  19. package/dist/catalog/normalize.d.ts +16 -0
  20. package/dist/catalog/normalize.js +115 -0
  21. package/dist/catalog/previews.d.ts +18 -0
  22. package/dist/catalog/previews.js +106 -0
  23. package/dist/catalog/renderers.d.ts +43 -0
  24. package/dist/catalog/renderers.js +62 -0
  25. package/dist/catalog/types.d.ts +63 -0
  26. package/dist/cli/auth/device.d.ts +6 -1
  27. package/dist/cli/auth/device.js +5 -2
  28. package/dist/cli/auth/store.d.ts +15 -6
  29. package/dist/cli/auth/store.js +72 -19
  30. package/dist/cli/make.d.ts +0 -5
  31. package/dist/cli/make.js +22 -67
  32. package/dist/cli/scaffold.js +25 -33
  33. package/dist/commands/checkout/index.d.ts +7 -4
  34. package/dist/commands/checkout/index.js +9 -6
  35. package/dist/commands/compat/push-args.d.ts +2 -2
  36. package/dist/commands/index.js +0 -2
  37. package/dist/commands/login/index.d.ts +5 -0
  38. package/dist/commands/login/index.js +18 -6
  39. package/dist/commands/logout/index.d.ts +5 -0
  40. package/dist/commands/logout/index.js +54 -15
  41. package/dist/commands/publish/index.d.ts +9 -6
  42. package/dist/commands/publish/index.js +11 -8
  43. package/dist/commands/pull/index.js +1 -1
  44. package/dist/commands/push/index.js +1 -1
  45. package/dist/commands/types.d.ts +2 -0
  46. package/dist/commands/utils.d.ts +36 -11
  47. package/dist/commands/utils.js +30 -16
  48. package/dist/config/catalog-include.d.ts +43 -0
  49. package/dist/config/catalog-include.js +203 -0
  50. package/dist/config/defaults.d.ts +3 -3
  51. package/dist/config/defaults.js +54 -48
  52. package/dist/config/env.d.ts +9 -0
  53. package/dist/config/env.js +43 -0
  54. package/dist/config/errors.d.ts +4 -0
  55. package/dist/config/errors.js +11 -0
  56. package/dist/config/from-env.d.ts +24 -0
  57. package/dist/config/from-env.js +7 -0
  58. package/dist/config/helpers.d.ts +2 -0
  59. package/dist/config/helpers.js +3 -1
  60. package/dist/config/index.d.ts +4 -3
  61. package/dist/config/index.js +10 -5
  62. package/dist/config/loader.d.ts +29 -11
  63. package/dist/config/loader.js +126 -45
  64. package/dist/config/resolve-env.d.ts +5 -0
  65. package/dist/config/resolve-env.js +102 -0
  66. package/dist/config/runtime.d.ts +7 -8
  67. package/dist/config/runtime.js +154 -154
  68. package/dist/config/validator.d.ts +2 -2
  69. package/dist/config/validator.js +2 -4
  70. package/dist/handlebars.d.ts +9 -0
  71. package/dist/handlebars.js +16 -0
  72. package/dist/index.d.ts +48 -8
  73. package/dist/index.js +88 -30
  74. package/dist/pattern.d.ts +7 -0
  75. package/dist/pattern.js +13 -0
  76. package/dist/pipeline/index.d.ts +1 -1
  77. package/dist/pipeline/index.js +1 -1
  78. package/dist/react.d.ts +43 -0
  79. package/dist/react.js +11 -0
  80. package/dist/registry/asset-storage/adapters/vercel-blob.js +1 -1
  81. package/dist/registry/asset-storage/define.d.ts +1 -1
  82. package/dist/registry/asset-storage/define.js +1 -1
  83. package/dist/registry/asset-storage/resolve.d.ts +2 -2
  84. package/dist/registry/asset-storage/resolve.js +4 -1
  85. package/dist/registry/checkout/index.d.ts +9 -0
  86. package/dist/registry/checkout/index.js +339 -153
  87. package/dist/registry/connection.d.ts +28 -24
  88. package/dist/registry/connection.js +53 -67
  89. package/dist/registry/db/driver.d.ts +4 -11
  90. package/dist/registry/db/driver.js +4 -16
  91. package/dist/registry/db/index.d.ts +1 -1
  92. package/dist/registry/db/index.js +1 -2
  93. package/dist/registry/db/schema.d.ts +34 -0
  94. package/dist/registry/db/schema.js +2 -0
  95. package/dist/registry/publish/index.d.ts +13 -2
  96. package/dist/registry/publish/index.js +113 -29
  97. package/dist/registry/publish/package.js +14 -1
  98. package/dist/registry/selection.d.ts +20 -0
  99. package/dist/registry/selection.js +37 -1
  100. package/dist/store/filesystem.d.ts +3 -3
  101. package/dist/store/filesystem.js +7 -7
  102. package/dist/store/registry.js +24 -3
  103. package/dist/transformers/plugins/csf-render.js +4 -2
  104. package/dist/transformers/plugins/ssr-render.js +14 -5
  105. package/dist/transformers/preview/component/api.d.ts +8 -0
  106. package/dist/transformers/preview/component/api.js +11 -15
  107. package/dist/transformers/preview/component/artifacts.d.ts +1 -1
  108. package/dist/transformers/preview/component/artifacts.js +11 -7
  109. package/dist/transformers/preview/component/builder.js +15 -18
  110. package/dist/transformers/preview/component/css.d.ts +2 -0
  111. package/dist/transformers/preview/component/css.js +8 -11
  112. package/dist/transformers/preview/component/html.js +18 -16
  113. package/dist/transformers/preview/component/javascript.js +2 -4
  114. package/dist/transformers/preview/pattern/builder.js +31 -18
  115. package/dist/transformers/preview/types.d.ts +15 -1
  116. package/dist/types/config.d.ts +77 -40
  117. package/docs/api.md +34 -0
  118. package/docs/cli.md +3 -3
  119. package/drizzle/0006_add_component_source_format.sql +3 -0
  120. package/drizzle/meta/0006_snapshot.json +1941 -0
  121. package/drizzle/meta/_journal.json +8 -1
  122. package/package.json +34 -1
  123. package/src/app/components/Component/Preview.tsx +36 -50
  124. package/src/app/components/Markdown/CodeHighlight.tsx +73 -70
  125. package/src/app/components/context/PreviewContext.tsx +5 -5
  126. package/src/app/components/util/index.ts +7 -15
  127. package/src/app/lib/mcp/shape.ts +6 -5
  128. package/src/app/lib/registry-api/transfer.ts +7 -5
  129. package/src/app/pages/account/tokens.tsx +3 -1
  130. package/src/app-builder/build.ts +13 -8
  131. package/src/app-builder/client-config.ts +5 -3
  132. package/src/app-builder/config-diff/strategies/pattern.ts +1 -1
  133. package/src/app-builder/watchers/component.ts +3 -3
  134. package/src/app-builder/watchers/utils.ts +2 -3
  135. package/src/app-builder/watchers.ts +63 -57
  136. package/src/cache/build-cache.ts +16 -15
  137. package/src/catalog/README.md +92 -0
  138. package/src/catalog/define.ts +79 -0
  139. package/src/catalog/implementation.ts +145 -0
  140. package/src/catalog/index.ts +31 -0
  141. package/src/catalog/normalize.ts +143 -0
  142. package/src/catalog/previews.ts +111 -0
  143. package/src/catalog/renderers.ts +94 -0
  144. package/src/catalog/types.ts +74 -0
  145. package/src/cli/README.md +1 -1
  146. package/src/cli/auth/device.ts +8 -3
  147. package/src/cli/auth/store.ts +75 -18
  148. package/src/cli/make.ts +20 -72
  149. package/src/cli/scaffold.ts +25 -31
  150. package/src/commands/README.md +1 -1
  151. package/src/commands/checkout/index.ts +17 -7
  152. package/src/commands/compat/push-args.ts +2 -2
  153. package/src/commands/index.ts +0 -2
  154. package/src/commands/login/index.ts +20 -6
  155. package/src/commands/logout/index.ts +74 -20
  156. package/src/commands/publish/index.ts +19 -9
  157. package/src/commands/pull/index.ts +2 -2
  158. package/src/commands/push/index.ts +2 -2
  159. package/src/commands/types.ts +2 -0
  160. package/src/commands/utils.ts +36 -12
  161. package/src/config/README.md +63 -2
  162. package/src/config/catalog-include.ts +227 -0
  163. package/src/config/defaults.ts +24 -14
  164. package/src/config/env.ts +41 -0
  165. package/src/config/errors.ts +7 -0
  166. package/src/config/from-env.ts +26 -0
  167. package/src/config/helpers.ts +4 -2
  168. package/src/config/index.ts +7 -4
  169. package/src/config/loader.ts +168 -50
  170. package/src/config/resolve-env.ts +94 -0
  171. package/src/config/runtime.ts +188 -193
  172. package/src/config/validator.ts +4 -6
  173. package/src/handlebars.ts +18 -0
  174. package/src/index.d.ts +2 -2
  175. package/src/index.ts +124 -34
  176. package/src/pattern.ts +14 -0
  177. package/src/pipeline/index.ts +1 -1
  178. package/src/react.ts +52 -0
  179. package/src/registry/asset-storage/adapters/vercel-blob.ts +1 -1
  180. package/src/registry/asset-storage/define.ts +1 -1
  181. package/src/registry/asset-storage/resolve.ts +6 -3
  182. package/src/registry/checkout/index.ts +421 -157
  183. package/src/registry/connection.ts +72 -71
  184. package/src/registry/db/driver.ts +6 -17
  185. package/src/registry/db/index.ts +0 -1
  186. package/src/registry/db/schema.ts +2 -0
  187. package/src/registry/publish/index.ts +128 -31
  188. package/src/registry/publish/package.ts +14 -1
  189. package/src/registry/selection.ts +44 -0
  190. package/src/store/filesystem.ts +6 -6
  191. package/src/store/registry.ts +13 -2
  192. package/src/transformers/plugins/csf-render.ts +4 -2
  193. package/src/transformers/plugins/ssr-render.ts +15 -5
  194. package/src/transformers/preview/component/api.ts +13 -1
  195. package/src/transformers/preview/component/artifacts.ts +8 -4
  196. package/src/transformers/preview/component/builder.ts +17 -4
  197. package/src/transformers/preview/component/css.ts +6 -10
  198. package/src/transformers/preview/component/html.ts +30 -19
  199. package/src/transformers/preview/component/javascript.ts +1 -3
  200. package/src/transformers/preview/pattern/builder.ts +33 -20
  201. package/src/transformers/preview/types.ts +15 -1
  202. package/src/types/config.ts +76 -40
  203. package/templates/default/components/button/button.react.handoff.js.tpl +19 -23
  204. package/templates/default/components/button/button.react.handoff.ts.tpl +22 -22
  205. package/templates/default/env.tpl +10 -0
  206. package/templates/default/gitignore.tpl +7 -0
  207. package/templates/default/handoff.config.js.tpl +21 -182
  208. package/templates/default/handoff.config.ts.tpl +21 -166
  209. package/templates/default/handoff.config.with-examples.js.tpl +22 -180
  210. package/templates/default/handoff.config.with-examples.ts.tpl +22 -164
  211. package/config/templates/component/template.json +0 -49
  212. package/config/templates/template.html +0 -7
  213. package/dist/commands/make/template.d.ts +0 -8
  214. package/dist/commands/make/template.js +0 -43
  215. package/dist/config/entries.d.ts +0 -47
  216. package/dist/config/entries.js +0 -162
  217. package/dist/config/normalizers/declaration.d.ts +0 -11
  218. package/dist/config/normalizers/declaration.js +0 -122
  219. package/dist/config/normalizers/pattern.d.ts +0 -8
  220. package/dist/config/normalizers/pattern.js +0 -41
  221. package/dist/declarations/helpers.d.ts +0 -6
  222. package/dist/declarations/helpers.js +0 -19
  223. package/dist/declarations/index.d.ts +0 -2
  224. package/dist/declarations/index.js +0 -9
  225. package/dist/declarations/types.d.ts +0 -60
  226. package/src/commands/make/template.ts +0 -46
  227. package/src/config/entries.ts +0 -175
  228. package/src/config/normalizers/declaration.ts +0 -179
  229. package/src/config/normalizers/pattern.ts +0 -59
  230. package/src/declarations/helpers.ts +0 -36
  231. package/src/declarations/index.ts +0 -19
  232. package/src/declarations/types.ts +0 -70
  233. /package/dist/{declarations → catalog}/types.js +0 -0
@@ -377,13 +377,21 @@ After migrations and deployment, open \`/install\` immediately and create the in
377
377
  The installer verifies the deployment but never changes the schema. An exposed, uninstalled registry
378
378
  can be claimed by its first visitor, so do not leave this step unattended.
379
379
 
380
- Once installation completes, sign in and authorize a workspace:
380
+ Once installation completes, sign in and authorize a workspace. Either approve the CLI in the
381
+ browser:
381
382
 
382
383
  \`\`\`bash
383
384
  handoff-app login --url https://registry.example.com
384
385
  \`\`\`
385
386
 
386
- The browser approval issues a revocable, user-owned credential. The former
387
+ or create a token under Account > Access tokens and set it in the workspace environment:
388
+
389
+ \`\`\`bash
390
+ HANDOFF_REGISTRY_URL=https://registry.example.com
391
+ HANDOFF_REGISTRY_ACCESS_TOKEN=hnd_…
392
+ \`\`\`
393
+
394
+ Both produce the same revocable, user-owned credential. The former
387
395
  \`HANDOFF_REGISTRY_API_TOKEN\` fixed server secret is no longer accepted.
388
396
 
389
397
  ## Run it (self-hosting)
@@ -695,14 +703,12 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
695
703
  pendingHandlers: new Map(),
696
704
  runtimeComponentsWatcher: null,
697
705
  runtimeConfigurationWatcher: null,
698
- componentDirectoriesWatcher: null,
699
- patternDirectoriesWatcher: null,
706
+ catalogDirectoriesWatcher: null,
700
707
  };
701
708
  (0, watchers_1.watchPublicDirectory)(handoff, wss, state, chokidarConfig);
702
709
  (0, watchers_1.watchRuntimeComponents)(handoff, state, (0, watchers_1.getRuntimeComponentsPathsToWatch)(handoff));
703
710
  (0, watchers_1.watchRuntimeConfiguration)(handoff, state);
704
- (0, watchers_1.watchComponentDirectories)(handoff, state, chokidarConfig);
705
- (0, watchers_1.watchPatternDirectories)(handoff, state, chokidarConfig);
711
+ (0, watchers_1.watchCatalogDirectories)(handoff, state, chokidarConfig);
706
712
  (0, watchers_1.watchGlobalEntries)(handoff, state, chokidarConfig);
707
713
  (0, watchers_1.watchPages)(handoff, chokidarConfig);
708
714
  });
@@ -95,7 +95,9 @@ const persistClientConfig = (handoff_1, ...args_1) => __awaiter(void 0, [handoff
95
95
  // even when packaged from a connected project. Otherwise a workspace is connected whenever a
96
96
  // registry URL resolves — from config/env or a saved device login — so `login` alone surfaces
97
97
  // the publish/checkout UI without also requiring HANDOFF_REGISTRY_URL.
98
- const connected = mode === 'registry' ? false : Boolean((yield (0, connection_1.resolveAuthenticatedRegistryConnection)(handoff.config, handoff.workingPath)).url);
98
+ const connected = mode === 'registry'
99
+ ? false
100
+ : Boolean((yield (0, connection_1.resolveAuthenticatedRegistryConnection)(handoff.config, handoff.workingPath, handoff.getProfile())).url);
99
101
  clientConfig.runtime = Object.assign(Object.assign({}, clientConfig.runtime), { mode, connected });
100
102
  yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'client.config.json'), { config: clientConfig }, { spaces: 2 });
101
103
  yield fs_extra_1.default.writeJson(path_1.default.resolve(appPath, 'runtime.server.json'), buildServerRuntimeConfig(handoff.config, options.runtimeModeOverride), { spaces: 2 });
@@ -43,12 +43,12 @@ const snapshot_1 = require("../snapshot");
43
43
  * components. Outer key is component id, inner key is synthetic preview key.
44
44
  */
45
45
  const snapshotPatternSynthetics = (handoff, patternId) => {
46
- var _a, _b, _c, _d;
47
- const components = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.components) !== null && _c !== void 0 ? _c : {};
46
+ var _a, _b, _c;
47
+ const components = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components) !== null && _b !== void 0 ? _b : {};
48
48
  const prefix = `__pattern_${patternId}_`;
49
49
  const result = new Map();
50
50
  for (const [componentId, component] of Object.entries(components)) {
51
- const previews = (_d = component.internalPatternPreviews) !== null && _d !== void 0 ? _d : {};
51
+ const previews = (_c = component.internalPatternPreviews) !== null && _c !== void 0 ? _c : {};
52
52
  const entries = new Map();
53
53
  for (const [key, preview] of Object.entries(previews)) {
54
54
  if (key.startsWith(prefix)) {
@@ -23,11 +23,11 @@ const mapEntryTypeToSegment = (type) => {
23
23
  };
24
24
  exports.mapEntryTypeToSegment = mapEntryTypeToSegment;
25
25
  const resolveComponentIdForChangedFile = (handoff, changedFilePath) => {
26
- var _a, _b, _c, _d;
26
+ var _a, _b, _c;
27
27
  const normalizedChangedPath = (0, path_2.normalizePathForCompare)(changedFilePath);
28
- const runtimeComponents = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.components) !== null && _c !== void 0 ? _c : {};
28
+ const runtimeComponents = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components) !== null && _b !== void 0 ? _b : {};
29
29
  for (const [componentId, componentDef] of Object.entries(runtimeComponents)) {
30
- const entries = (_d = componentDef.entries) !== null && _d !== void 0 ? _d : {};
30
+ const entries = (_c = componentDef.entries) !== null && _c !== void 0 ? _c : {};
31
31
  for (const entryPath of Object.values(entries)) {
32
32
  if (!entryPath)
33
33
  continue;
@@ -49,8 +49,8 @@ exports.resolveComponentIdForChangedFile = resolveComponentIdForChangedFile;
49
49
  const getRuntimeComponentsPathsToWatch = (handoff) => {
50
50
  var _a, _b, _c;
51
51
  const result = new Map();
52
- for (const runtimeComponentId of Object.keys((_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries.components) !== null && _b !== void 0 ? _b : {})) {
53
- const runtimeComponent = handoff.runtimeConfig.entries.components[runtimeComponentId];
52
+ for (const runtimeComponentId of Object.keys((_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components) !== null && _b !== void 0 ? _b : {})) {
53
+ const runtimeComponent = handoff.runtimeConfig.entities.components[runtimeComponentId];
54
54
  for (const [runtimeComponentEntryType, runtimeComponentEntryPath] of Object.entries((_c = runtimeComponent.entries) !== null && _c !== void 0 ? _c : {})) {
55
55
  const normalizedComponentEntryPath = runtimeComponentEntryPath;
56
56
  if (fs_extra_1.default.existsSync(normalizedComponentEntryPath)) {
@@ -4,9 +4,8 @@ export interface WatcherState {
4
4
  pendingHandlers: Map<string, () => Promise<void>>;
5
5
  runtimeComponentsWatcher: chokidar.FSWatcher | null;
6
6
  runtimeConfigurationWatcher: chokidar.FSWatcher | null;
7
- componentDirectoriesWatcher: chokidar.FSWatcher | null;
8
- /** Watches entries.patterns parent directories for newly created pattern subdirectories. */
9
- patternDirectoriesWatcher: chokidar.FSWatcher | null;
7
+ /** Watches every registered catalog directory for newly created item subdirectories. */
8
+ catalogDirectoriesWatcher: chokidar.FSWatcher | null;
10
9
  }
11
10
  /**
12
11
  * Schedules a handler for execution with mutual exclusion and per-source
@@ -36,19 +36,12 @@ export declare const watchRuntimeComponents: (handoff: Handoff, state: WatcherSt
36
36
  */
37
37
  export declare const watchRuntimeConfiguration: (handoff: Handoff, state: WatcherState) => void;
38
38
  /**
39
- * Watches the parent component directories from config.entries.components for
40
- * new components being added. When a new config file appears in a new
41
- * subdirectory, reloads the runtime config, restarts the component and
42
- * configuration watchers, then builds the new component and any patterns that
43
- * reference it.
44
- */
45
- export declare const watchComponentDirectories: (handoff: Handoff, state: WatcherState, chokidarConfig: chokidar.WatchOptions) => void;
46
- /**
47
- * Watches the parent pattern directories from config.entries.patterns for new
48
- * patterns being added. The rebuild sequence intentionally differs from
49
- * watchComponentDirectories because the dependency arrow is reversed: a new
50
- * pattern references existing components, so we rebuild only the preview
51
- * segment of those components (to produce the __pattern_* HTML files that
52
- * injectPatternPreviews registered) before composing the pattern itself.
39
+ * Watches `catalog.include` directories for a new item. A single watcher covers implementations
40
+ * and compositions; the declaration determines which pipeline builds the item.
41
+ *
42
+ * The rebuild differs by kind, and the dependency arrow is reversed between them. A new item with
43
+ * an implementation builds itself, then the compositions that reference it. A new composition
44
+ * rebuilds the preview segment of the items it references, so the synthetic `__pattern_*` HTML
45
+ * exists, and only then composes itself.
53
46
  */
54
- export declare const watchPatternDirectories: (handoff: Handoff, state: WatcherState, chokidarConfig: chokidar.WatchOptions) => void;
47
+ export declare const watchCatalogDirectories: (handoff: Handoff, state: WatcherState, chokidarConfig: chokidar.WatchOptions) => void;
@@ -35,7 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
36
  };
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
- exports.watchPatternDirectories = exports.watchComponentDirectories = exports.watchRuntimeConfiguration = exports.watchRuntimeComponents = exports.watchGlobalEntries = exports.watchPages = exports.watchAppSource = exports.watchPublicDirectory = exports.mapEntryTypeToSegment = exports.getRuntimeComponentsPathsToWatch = void 0;
38
+ exports.watchCatalogDirectories = exports.watchRuntimeConfiguration = exports.watchRuntimeComponents = exports.watchGlobalEntries = exports.watchPages = exports.watchAppSource = exports.watchPublicDirectory = exports.mapEntryTypeToSegment = exports.getRuntimeComponentsPathsToWatch = void 0;
39
39
  const chokidar_1 = __importDefault(require("chokidar"));
40
40
  const fs_extra_1 = __importDefault(require("fs-extra"));
41
41
  const path_1 = __importDefault(require("path"));
@@ -228,9 +228,9 @@ const watchRuntimeComponents = (handoff, state, runtimeComponentPathsToWatch) =>
228
228
  };
229
229
  exports.watchRuntimeComponents = watchRuntimeComponents;
230
230
  const rebuildPatternComponentPreviews = (handoff, patternId) => __awaiter(void 0, void 0, void 0, function* () {
231
- var _a, _b, _c, _d;
232
- const pattern = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.patterns) === null || _c === void 0 ? void 0 : _c[patternId];
233
- if (!((_d = pattern === null || pattern === void 0 ? void 0 : pattern.components) === null || _d === void 0 ? void 0 : _d.length))
231
+ var _a, _b;
232
+ const pattern = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.patterns[patternId];
233
+ if (!((_b = pattern === null || pattern === void 0 ? void 0 : pattern.components) === null || _b === void 0 ? void 0 : _b.length))
234
234
  return;
235
235
  for (const ref of pattern.components) {
236
236
  yield (0, builder_1.default)(handoff, ref.id, builder_1.ComponentSegment.Previews);
@@ -264,18 +264,40 @@ const watchRuntimeConfiguration = (handoff, state) => {
264
264
  const entryBefore = handoff.getConfigFileEntry(changedFile);
265
265
  const strategy = entryBefore ? (0, config_diff_1.getStrategy)(entryBefore.kind) : undefined;
266
266
  const handle = entryBefore && strategy ? strategy.capture(handoff, entryBefore.entityId) : undefined;
267
- handoff.reload();
268
- yield (0, client_config_1.persistClientConfig)(handoff);
267
+ let reloadFailed = false;
268
+ try {
269
+ handoff.reload();
270
+ yield (0, client_config_1.persistClientConfig)(handoff);
271
+ }
272
+ catch (e) {
273
+ // A failed reload leaves the last config that loaded on the instance, so the
274
+ // re-arm below still names the file that just broke. Without it the file drops
275
+ // out of the watch set - chokidar stops tracking a watched file once it is
276
+ // deleted - and a later valid save is never seen.
277
+ reloadFailed = true;
278
+ logger_1.Logger.error('Error reloading runtime configuration:', e);
279
+ }
269
280
  (0, exports.watchRuntimeComponents)(handoff, state, (0, component_1.getRuntimeComponentsPathsToWatch)(handoff));
270
281
  (0, exports.watchRuntimeConfiguration)(handoff, state);
282
+ if (reloadFailed) {
283
+ return;
284
+ }
271
285
  const entryAfter = handoff.getConfigFileEntry(changedFile);
272
- const normalizedChanged = (0, path_2.normalizePathForCompare)(changedFile);
273
- const normalizedMainConfig = handoff.getMainConfigFilePath()
274
- ? (0, path_2.normalizePathForCompare)(handoff.getMainConfigFilePath())
275
- : undefined;
276
- if (normalizedMainConfig && normalizedChanged === normalizedMainConfig) {
286
+ const kindChanged = !!entryBefore && !!entryAfter && entryBefore.kind !== entryAfter.kind;
287
+ if (handoff.isMainConfigFile(changedFile)) {
277
288
  yield (0, builder_1.default)(handoff);
278
289
  }
290
+ else if (kindChanged) {
291
+ // The declaration switched between an implementation and a composition. A targeted
292
+ // rebuild would leave the previous lane's summary entry and artifacts behind, so
293
+ // rebuild everything and let the finalizers re-sync.
294
+ yield (0, builder_1.default)(handoff);
295
+ }
296
+ else if ((entryAfter === null || entryAfter === void 0 ? void 0 : entryAfter.kind) === 'unknown') {
297
+ // The file did not parse. It stays watched, so the next valid save rebuilds it.
298
+ // Guessing a lane here would rebuild the wrong entity.
299
+ logger_1.Logger.warn(`Declaration is not valid yet; skipping rebuild: ${changedFile}`);
300
+ }
279
301
  else if (handle) {
280
302
  yield handle.apply(handoff, entryAfter === null || entryAfter === void 0 ? void 0 : entryAfter.entityId);
281
303
  }
@@ -319,9 +341,14 @@ const watchEntityDirectories = (handoff, state, chokidarConfig, options) => {
319
341
  if (configPaths.length === 0)
320
342
  return;
321
343
  const dirsToWatch = [];
344
+ const seenDirs = new Set();
322
345
  for (const configPath of configPaths) {
323
346
  const resolved = path_1.default.resolve(handoff.workingPath, configPath);
347
+ const key = (0, path_2.normalizePathForCompare)(resolved);
348
+ if (seenDirs.has(key))
349
+ continue;
324
350
  if (fs_extra_1.default.existsSync(resolved) && fs_extra_1.default.statSync(resolved).isDirectory()) {
351
+ seenDirs.add(key);
325
352
  dirsToWatch.push(resolved);
326
353
  }
327
354
  }
@@ -335,13 +362,8 @@ const watchEntityDirectories = (handoff, state, chokidarConfig, options) => {
335
362
  watcher.on('add', (file) => __awaiter(void 0, void 0, void 0, function* () {
336
363
  const basename = path_1.default.basename(file);
337
364
  const dirName = path_1.default.basename(path_1.default.dirname(file));
338
- // Only react to the primary declaration file for the directory (e.g.
339
- // button.json, button.js, or button.handoff.ts inside a button/ subdir).
340
- // The basename.startsWith(dirName) guard below ensures .ts here only
341
- // ever matches files named after their parent directory, not arbitrary
342
- // TypeScript source files.
343
- const isConfigFile = basename.endsWith('.json') || basename.endsWith('.js') || basename.endsWith('.cjs') || basename.endsWith('.ts');
344
- const isNewEntity = isConfigFile && basename.startsWith(dirName) && !knownIds.has(dirName);
365
+ const isConfigFile = /\.handoff\.(ts|js|cjs)$/.test(basename);
366
+ const isNewEntity = isConfigFile && !knownIds.has(dirName);
345
367
  if (!isNewEntity)
346
368
  return;
347
369
  yield (0, utils_1.scheduleHandler)(state, `${scheduleKeyPrefix}:${dirName}`, () => __awaiter(void 0, void 0, void 0, function* () {
@@ -380,57 +402,44 @@ const watchEntityDirectories = (handoff, state, chokidarConfig, options) => {
380
402
  }));
381
403
  };
382
404
  /**
383
- * Watches the parent component directories from config.entries.components for
384
- * new components being added. When a new config file appears in a new
385
- * subdirectory, reloads the runtime config, restarts the component and
386
- * configuration watchers, then builds the new component and any patterns that
387
- * reference it.
405
+ * Watches `catalog.include` directories for a new item. A single watcher covers implementations
406
+ * and compositions; the declaration determines which pipeline builds the item.
407
+ *
408
+ * The rebuild differs by kind, and the dependency arrow is reversed between them. A new item with
409
+ * an implementation builds itself, then the compositions that reference it. A new composition
410
+ * rebuilds the preview segment of the items it references, so the synthetic `__pattern_*` HTML
411
+ * exists, and only then composes itself.
388
412
  */
389
- const watchComponentDirectories = (handoff, state, chokidarConfig) => {
413
+ const watchCatalogDirectories = (handoff, state, chokidarConfig) => {
390
414
  watchEntityDirectories(handoff, state, chokidarConfig, {
391
- getConfigPaths: (h) => { var _a, _b; return (_b = (_a = h.config.entries) === null || _a === void 0 ? void 0 : _a.components) !== null && _b !== void 0 ? _b : []; },
392
- getKnownIds: (h) => { var _a, _b, _c; return Object.keys((_c = (_b = (_a = h.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.components) !== null && _c !== void 0 ? _c : {}); },
393
- getWatcher: (s) => s.componentDirectoriesWatcher,
394
- setWatcher: (s, w) => { s.componentDirectoriesWatcher = w; },
395
- scheduleKeyPrefix: 'newComponent',
396
- entityLabel: 'component',
415
+ getConfigPaths: (h) => { var _a, _b; return [...((_b = (_a = h.config.catalog) === null || _a === void 0 ? void 0 : _a.include) !== null && _b !== void 0 ? _b : [])]; },
416
+ getKnownIds: (h) => {
417
+ var _a, _b, _c, _d;
418
+ return [
419
+ ...Object.keys((_b = (_a = h.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components) !== null && _b !== void 0 ? _b : {}),
420
+ ...Object.keys((_d = (_c = h.runtimeConfig) === null || _c === void 0 ? void 0 : _c.entities.patterns) !== null && _d !== void 0 ? _d : {}),
421
+ ];
422
+ },
423
+ getWatcher: (s) => s.catalogDirectoriesWatcher,
424
+ setWatcher: (s, w) => {
425
+ s.catalogDirectoriesWatcher = w;
426
+ },
427
+ scheduleKeyPrefix: 'newCatalogItem',
428
+ entityLabel: 'catalog item',
397
429
  onDetected: (handoff_1, _a) => __awaiter(void 0, [handoff_1, _a], void 0, function* (handoff, { addedIds }) {
398
- for (const componentId of addedIds) {
430
+ const addedComponentIds = addedIds.filter((id) => { var _a; return !!((_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components[id]); });
431
+ const addedPatternIds = addedIds.filter((id) => { var _a; return !!((_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.patterns[id]); });
432
+ for (const componentId of addedComponentIds) {
399
433
  yield (0, builder_1.default)(handoff, componentId);
400
434
  yield (0, config_diff_1.runAllFinalizers)(handoff, { patternRebuildComponentIds: [componentId] });
401
435
  }
402
- }),
403
- });
404
- };
405
- exports.watchComponentDirectories = watchComponentDirectories;
406
- /**
407
- * Watches the parent pattern directories from config.entries.patterns for new
408
- * patterns being added. The rebuild sequence intentionally differs from
409
- * watchComponentDirectories because the dependency arrow is reversed: a new
410
- * pattern references existing components, so we rebuild only the preview
411
- * segment of those components (to produce the __pattern_* HTML files that
412
- * injectPatternPreviews registered) before composing the pattern itself.
413
- */
414
- const watchPatternDirectories = (handoff, state, chokidarConfig) => {
415
- watchEntityDirectories(handoff, state, chokidarConfig, {
416
- getConfigPaths: (h) => { var _a, _b; return (_b = (_a = h.config.entries) === null || _a === void 0 ? void 0 : _a.patterns) !== null && _b !== void 0 ? _b : []; },
417
- getKnownIds: (h) => { var _a, _b, _c; return Object.keys((_c = (_b = (_a = h.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.patterns) !== null && _c !== void 0 ? _c : {}); },
418
- getWatcher: (s) => s.patternDirectoriesWatcher,
419
- setWatcher: (s, w) => { s.patternDirectoriesWatcher = w; },
420
- scheduleKeyPrefix: 'newPattern',
421
- entityLabel: 'pattern',
422
- onDetected: (handoff_1, _a) => __awaiter(void 0, [handoff_1, _a], void 0, function* (handoff, { addedIds }) {
423
- // Rebuild only the previews segment for each component the new pattern
424
- // references. Their JS/CSS/structure are unchanged — only the new
425
- // synthetic preview HTML files need to be generated so that buildPatterns
426
- // can assemble the pattern from them.
427
- for (const patternId of addedIds) {
436
+ for (const patternId of addedPatternIds) {
428
437
  yield rebuildPatternComponentPreviews(handoff, patternId);
429
438
  }
430
- // Build only the new pattern directly rather than going through
431
- // runAllFinalizers, which would rebuild all patterns unnecessarily.
432
- yield (0, patterns_1.buildPatterns)(handoff, { onlyPatternIds: new Set(addedIds) });
439
+ if (addedPatternIds.length > 0) {
440
+ yield (0, patterns_1.buildPatterns)(handoff, { onlyPatternIds: new Set(addedPatternIds) });
441
+ }
433
442
  }),
434
443
  });
435
444
  };
436
- exports.watchPatternDirectories = watchPatternDirectories;
445
+ exports.watchCatalogDirectories = watchCatalogDirectories;
@@ -35,7 +35,11 @@ const CACHE_VERSION = '1.0.0';
35
35
  * Gets the path to the build cache file
36
36
  */
37
37
  function getCachePath(handoff) {
38
- return path_1.default.resolve(handoff.modulePath, '.handoff', handoff.getProjectId(), '.cache', 'build-cache.json');
38
+ // Two profiles share one project id and can build different output, so the profile has to name
39
+ // the cache file. One shared cache would serve the other profile's components.
40
+ const profile = handoff.getProfile();
41
+ const fileName = profile ? `build-cache.${profile}.json` : 'build-cache.json';
42
+ return path_1.default.resolve(handoff.modulePath, '.handoff', handoff.getProjectId(), '.cache', fileName);
39
43
  }
40
44
  /**
41
45
  * Loads the build cache from disk
@@ -136,14 +140,20 @@ function haveGlobalDepsChanged(cached, current) {
136
140
  * Gets all file paths that should be tracked for a component
137
141
  */
138
142
  function getComponentFilePaths(handoff, componentId) {
139
- var _a, _b, _c, _d, _e, _f, _g;
140
- const runtimeComponent = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.components) === null || _c === void 0 ? void 0 : _c[componentId];
143
+ var _a, _b, _c, _d;
144
+ const runtimeComponent = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components[componentId];
141
145
  if (!runtimeComponent) {
142
146
  return { files: [] };
143
147
  }
144
148
  const files = [];
145
149
  let templateDir;
146
150
  const componentDirs = new Set();
151
+ // A catalog item declares its previews in the declaration file, and its implementation can sit
152
+ // outside the item directory. The directories derived from `entries` below can therefore miss the
153
+ // declaration, which would leave a preview edit uncached.
154
+ if (runtimeComponent.path) {
155
+ componentDirs.add((0, path_2.normalizePathForCompare)(runtimeComponent.path));
156
+ }
147
157
  // Add entry files and infer component directories from resolved entry paths
148
158
  const entries = runtimeComponent.entries;
149
159
  if (entries) {
@@ -169,7 +179,7 @@ function getComponentFilePaths(handoff, componentId) {
169
179
  componentDirs.add((0, path_2.normalizePathForCompare)(path_1.default.dirname(templatePath)));
170
180
  }
171
181
  }
172
- catch (_h) {
182
+ catch (_e) {
173
183
  // File doesn't exist, still add to track
174
184
  files.push(templatePath);
175
185
  componentDirs.add((0, path_2.normalizePathForCompare)(path_1.default.dirname(templatePath)));
@@ -178,19 +188,9 @@ function getComponentFilePaths(handoff, componentId) {
178
188
  }
179
189
  // Find the config file path for this component using exact config filename + directory matching.
180
190
  const configPaths = handoff.getConfigFilePaths();
181
- const expectedConfigFileNames = new Set([
182
- `${componentId}.handoff.ts`,
183
- `${componentId}.handoff.js`,
184
- `${componentId}.handoff.cjs`,
185
- `${componentId}.handoff.json`,
186
- `${componentId}.json`,
187
- `${componentId}.js`,
188
- `${componentId}.cjs`,
189
- ]);
190
191
  const matchingConfigPath = configPaths.find((configPath) => {
191
192
  const configFileName = path_1.default.basename(configPath);
192
- const isModernDeclaration = /\.handoff\.(ts|js|cjs|json)$/.test(configFileName);
193
- if (!expectedConfigFileNames.has(configFileName) && !isModernDeclaration) {
193
+ if (!/\.handoff\.(ts|js|cjs)$/.test(configFileName)) {
194
194
  return false;
195
195
  }
196
196
  if (componentDirs.size === 0) {
@@ -208,7 +208,7 @@ function getComponentFilePaths(handoff, componentId) {
208
208
  // what processComponents must render. Without tracking these files here, the
209
209
  // cache would incorrectly consider the component unchanged and skip the preview
210
210
  // rebuild, leaving buildPatterns unable to find the required HTML fragments.
211
- const runtimePatterns = (_f = (_e = (_d = handoff.runtimeConfig) === null || _d === void 0 ? void 0 : _d.entries) === null || _e === void 0 ? void 0 : _e.patterns) !== null && _f !== void 0 ? _f : {};
211
+ const runtimePatterns = (_c = (_b = handoff.runtimeConfig) === null || _b === void 0 ? void 0 : _b.entities.patterns) !== null && _c !== void 0 ? _c : {};
212
212
  for (const configPath of configPaths) {
213
213
  const entry = handoff.getConfigFileEntry(configPath);
214
214
  if ((entry === null || entry === void 0 ? void 0 : entry.kind) !== 'pattern')
@@ -216,7 +216,7 @@ function getComponentFilePaths(handoff, componentId) {
216
216
  const pattern = runtimePatterns[entry.entityId];
217
217
  if (!pattern)
218
218
  continue;
219
- if ((_g = pattern.components) === null || _g === void 0 ? void 0 : _g.some((ref) => ref.id === componentId)) {
219
+ if ((_d = pattern.components) === null || _d === void 0 ? void 0 : _d.some((ref) => ref.id === componentId)) {
220
220
  files.push(configPath);
221
221
  }
222
222
  }
@@ -280,24 +280,24 @@ function hasComponentChanged(cached, current) {
280
280
  */
281
281
  function checkOutputExists(handoff, componentId) {
282
282
  return __awaiter(this, void 0, void 0, function* () {
283
- var _a, _b, _c, _d, _e, _f;
283
+ var _a, _b, _c, _d;
284
284
  const outputDirectory = path_1.default.resolve(handoff.workingPath, 'public/api/component');
285
285
  const componentPath = path_1.default.resolve(outputDirectory, `${componentId}.json`);
286
286
  if (!(yield fs_extra_1.default.pathExists(componentPath))) {
287
287
  return false;
288
288
  }
289
- const runtimeComponent = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.components) === null || _c === void 0 ? void 0 : _c[componentId];
289
+ const runtimeComponent = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entities.components[componentId];
290
290
  const previewIds = new Set([
291
- ...Object.keys((_d = runtimeComponent === null || runtimeComponent === void 0 ? void 0 : runtimeComponent.previews) !== null && _d !== void 0 ? _d : {}),
292
- ...Object.keys((_e = runtimeComponent === null || runtimeComponent === void 0 ? void 0 : runtimeComponent.internalPatternPreviews) !== null && _e !== void 0 ? _e : {}),
291
+ ...Object.keys((_b = runtimeComponent === null || runtimeComponent === void 0 ? void 0 : runtimeComponent.previews) !== null && _b !== void 0 ? _b : {}),
292
+ ...Object.keys((_c = runtimeComponent === null || runtimeComponent === void 0 ? void 0 : runtimeComponent.internalPatternPreviews) !== null && _c !== void 0 ? _c : {}),
293
293
  ]);
294
294
  try {
295
295
  const component = yield fs_extra_1.default.readJson(componentPath);
296
- for (const previewId of Object.keys((_f = component === null || component === void 0 ? void 0 : component.previews) !== null && _f !== void 0 ? _f : {})) {
296
+ for (const previewId of Object.keys((_d = component === null || component === void 0 ? void 0 : component.previews) !== null && _d !== void 0 ? _d : {})) {
297
297
  previewIds.add(previewId);
298
298
  }
299
299
  }
300
- catch (_g) {
300
+ catch (_e) {
301
301
  return false;
302
302
  }
303
303
  for (const previewId of previewIds) {
@@ -0,0 +1,6 @@
1
+ import { type RendererKind } from './renderers';
2
+ import type { CatalogItem, CatalogItemInput } from './types';
3
+ /** Keeps declarations as plain data that the loader can read without evaluating Handoff. */
4
+ export declare const createCatalogItem: <TArgs = Record<string, unknown>>(input: CatalogItemInput, renderer?: RendererKind) => CatalogItem<TArgs>;
5
+ /** Validate module declarations too, because JavaScript callers have no compile-time checks. */
6
+ export declare function validateCatalogItem(value: unknown): asserts value is CatalogItem;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCatalogItem = void 0;
4
+ exports.validateCatalogItem = validateCatalogItem;
5
+ const renderers_1 = require("./renderers");
6
+ const MISSING_RENDERER_HELP = "An implementation needs a renderer. Import defineCatalogItem from 'handoff-app/react' or 'handoff-app/handlebars'.";
7
+ /** Handles the shared implementation forms here so renderer entry points only supply types and a renderer. */
8
+ const stampImplementation = (implementation, renderer) => {
9
+ if (typeof implementation === 'string') {
10
+ return { renderer, file: implementation };
11
+ }
12
+ const descriptor = implementation;
13
+ if (descriptor && typeof descriptor === 'object' && typeof descriptor.format === 'string' && typeof descriptor.file === 'string') {
14
+ return { renderer, format: descriptor.format, file: descriptor.file };
15
+ }
16
+ return { renderer, value: implementation };
17
+ };
18
+ /** Keeps declarations as plain data that the loader can read without evaluating Handoff. */
19
+ const createCatalogItem = (input, renderer) => {
20
+ const hasImplementation = input.implementation !== undefined && input.implementation !== null;
21
+ const hasComposition = Array.isArray(input.composition) && input.composition.length > 0;
22
+ if (hasImplementation && hasComposition) {
23
+ throw new Error('A catalog item declares either "implementation" or "composition", not both.');
24
+ }
25
+ if (!hasImplementation && !hasComposition) {
26
+ throw new Error('A catalog item must declare "implementation" or "composition".');
27
+ }
28
+ if (!hasImplementation) {
29
+ validateCatalogItem(input);
30
+ return input;
31
+ }
32
+ if (!renderer) {
33
+ throw new Error(MISSING_RENDERER_HELP);
34
+ }
35
+ const item = Object.assign(Object.assign({}, input), { implementation: stampImplementation(input.implementation, renderer) });
36
+ validateCatalogItem(item);
37
+ return item;
38
+ };
39
+ exports.createCatalogItem = createCatalogItem;
40
+ /** Validate module declarations too, because JavaScript callers have no compile-time checks. */
41
+ function validateCatalogItem(value) {
42
+ const help = ' Use defineCatalogItem. See UPGRADE.md#catalog-items.';
43
+ if (!value || typeof value !== 'object' || Array.isArray(value))
44
+ throw new Error('Invalid catalog item.' + help);
45
+ const item = value;
46
+ for (const key of ['previews', 'components', 'renderer', 'sourceFormat', 'should_do', 'should_not_do', 'title']) {
47
+ if (key in item)
48
+ throw new Error(`Catalog field "${key}" is no longer supported.` + help);
49
+ }
50
+ for (const key of ['component', 'story', 'template']) {
51
+ if (item.entries && key in item.entries)
52
+ throw new Error(`entries.${key} was removed. Use implementation.` + help);
53
+ }
54
+ if (item.composition !== undefined) {
55
+ if (item.implementation !== undefined || !Array.isArray(item.composition) || !item.composition.length) {
56
+ throw new Error('Declare either an implementation or a non-empty composition.' + help);
57
+ }
58
+ for (const ref of item.composition) {
59
+ if (!ref || typeof ref.ref !== 'string' || !ref.ref.trim() || 'id' in ref) {
60
+ throw new Error('Composition members require "ref", the catalog item id.' + help);
61
+ }
62
+ }
63
+ return;
64
+ }
65
+ const source = item.implementation;
66
+ if (!source || !(0, renderers_1.isRendererKind)(source.renderer))
67
+ throw new Error('implementation must state a supported renderer.' + help);
68
+ if (source.format !== undefined &&
69
+ (!(0, renderers_1.isSourceFormat)(source.format) || !renderers_1.SOURCE_FORMATS[source.format].renderers.includes(source.renderer))) {
70
+ throw new Error('implementation.format is not supported by this renderer.' + help);
71
+ }
72
+ }
@@ -0,0 +1,27 @@
1
+ /** A source file plus the export the implementation is bound to. */
2
+ export type ResolvedSource = {
3
+ file: string;
4
+ /** `default`, a named export, or `*` for a namespace import. */
5
+ exportName: string;
6
+ };
7
+ /**
8
+ * Resolves a relative module specifier to a file. Node and bundlers probe extensions and index
9
+ * files; `path.resolve` alone does not. A bare specifier (a package, or a tsconfig path alias) is
10
+ * not resolvable against the workspace, so it returns undefined.
11
+ */
12
+ export declare const resolveModulePath: (specifier: string, fromFile: string) => string | undefined;
13
+ /**
14
+ * Resolves the value a property is bound to back to its source file.
15
+ *
16
+ * Two callers use this: `implementation:` inside a catalog declaration, and `component:` inside a
17
+ * CSF meta object. In both cases only the value is authored, so the file has to be recovered from
18
+ * the import that produced it.
19
+ */
20
+ export declare const resolvePropertySource: (sourcePath: string, property: string) => ResolvedSource | undefined;
21
+ /**
22
+ * Last-resort lookup when the import cannot be resolved: the single component file sitting next to
23
+ * the declaration. Story files and the declaration itself are excluded.
24
+ */
25
+ export declare const findSiblingComponentFile: (directory: string, declarationPath: string) => string | undefined;
26
+ /** True when `file` sits inside `directory`. Publish rejects any path that escapes the entity directory. */
27
+ export declare const isInsideDirectory: (file: string, directory: string) => boolean;