@kb-labs/rest-api-core 1.0.0 → 1.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/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { ManifestV3 } from '@kb-labs/plugin-contracts';
3
- import { StudioPluginEntry, StudioWidgetDecl, StudioMenuDecl, StudioLayoutDecl, StudioRegistry } from '@kb-labs/rest-api-contracts';
3
+ import { StudioPluginEntryV2, StudioRegistryV2 } from '@kb-labs/rest-api-contracts';
4
4
 
5
5
  /**
6
6
  * @module @kb-labs/rest-api-core/config/schema
@@ -230,22 +230,26 @@ declare function loadRestApiConfig(cwd?: string, cliOverrides?: Partial<RestApiC
230
230
  /**
231
231
  * @module @kb-labs/rest-api-core/studio-registry
232
232
  *
233
- * Transforms ManifestV3 studio sections into StudioRegistry format.
234
- * REST API uses this to provide pre-computed registry data to Studio frontend.
233
+ * Transforms ManifestV3 studio sections into StudioRegistryV2 format.
234
+ *
235
+ * Each plugin entry includes:
236
+ * - remoteEntryUrl: browser-facing URL (proxied through gateway)
237
+ * - widgetBundleDir: absolute filesystem path to dist/widgets/ (used by gateway to serve files)
235
238
  */
236
239
 
237
240
  /**
238
- * Convert a single ManifestV3 to StudioRegistry entries.
241
+ * Convert a single ManifestV3 with studio V2 config to a registry entry.
242
+ *
243
+ * @param manifest - Plugin manifest with studio V2 config
244
+ * @param pluginRoot - Absolute path to the plugin package root (where package.json lives)
239
245
  */
240
- declare function manifestToRegistry(manifest: ManifestV3): {
241
- plugin: StudioPluginEntry;
242
- widgets: StudioWidgetDecl[];
243
- menus: StudioMenuDecl[];
244
- layouts: StudioLayoutDecl[];
245
- };
246
+ declare function manifestToRegistryEntry(manifest: ManifestV3, pluginRoot: string): StudioPluginEntryV2 | null;
246
247
  /**
247
- * Combine multiple manifests into a single StudioRegistry.
248
+ * Combine multiple manifests into a StudioRegistryV2.
248
249
  */
249
- declare function combineManifestsToRegistry(manifests: ManifestV3[], _registryVersion?: string): StudioRegistry;
250
+ declare function combineManifestsToRegistry(manifests: Array<{
251
+ manifest: ManifestV3;
252
+ pluginRoot: string;
253
+ }>): StudioRegistryV2;
250
254
 
251
- export { type RestApiConfig, combineManifestsToRegistry, loadRestApiConfig, manifestToRegistry, restApiConfigSchema };
255
+ export { type RestApiConfig, combineManifestsToRegistry, loadRestApiConfig, manifestToRegistryEntry, restApiConfigSchema };
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { loadBundle } from '@kb-labs/core-bundle';
2
2
  import { findNearestConfig, readJsonWithDiagnostics, resolveConfig } from '@kb-labs/core-config';
3
3
  import { z } from 'zod';
4
+ import { join } from 'path';
5
+ import { statSync } from 'fs';
4
6
 
5
7
  // src/config/loader.ts
6
8
  var restApiConfigSchema = z.object({
@@ -201,53 +203,46 @@ async function loadRestApiConfig(cwd = process.cwd(), cliOverrides) {
201
203
  diagnostics: resolved.diagnostics
202
204
  };
203
205
  }
204
-
205
- // src/studio-registry.ts
206
- function manifestToRegistry(manifest) {
206
+ function getRemoteEntryVersion(widgetBundleDir) {
207
+ try {
208
+ const stat = statSync(join(widgetBundleDir, "remoteEntry.js"));
209
+ return String(Math.floor(stat.mtimeMs));
210
+ } catch {
211
+ return "0";
212
+ }
213
+ }
214
+ function manifestToRegistryEntry(manifest, pluginRoot) {
207
215
  const studio = manifest.studio;
208
- if (!studio) {
209
- return {
210
- plugin: {
211
- pluginId: manifest.id,
212
- displayName: manifest.display?.name,
213
- widgets: [],
214
- menus: [],
215
- layouts: []
216
- },
217
- widgets: [],
218
- menus: [],
219
- layouts: []
220
- };
216
+ if (!studio || studio.version !== 2) {
217
+ return null;
221
218
  }
222
- const widgets = studio.widgets ?? [];
223
- const menus = studio.menus ?? [];
224
- const layouts = studio.layouts ?? [];
225
- const plugin = {
219
+ return {
226
220
  pluginId: manifest.id,
227
221
  displayName: manifest.display?.name,
228
- widgets,
229
- menus,
230
- layouts
222
+ pluginVersion: manifest.version,
223
+ remoteName: studio.remoteName,
224
+ remoteEntryUrl: `/plugins/${manifest.id}/widgets/remoteEntry.js?v=${getRemoteEntryVersion(join(pluginRoot, "dist", "widgets"))}`,
225
+ widgetBundleDir: join(pluginRoot, "dist", "widgets"),
226
+ pages: studio.pages ?? [],
227
+ menus: studio.menus ?? []
231
228
  };
232
- return { plugin, widgets, menus, layouts };
233
229
  }
234
- function combineManifestsToRegistry(manifests, _registryVersion) {
230
+ function combineManifestsToRegistry(manifests) {
235
231
  const plugins = [];
236
- for (const manifest of manifests) {
237
- if (!manifest.studio) {
238
- continue;
232
+ for (const { manifest, pluginRoot } of manifests) {
233
+ const entry = manifestToRegistryEntry(manifest, pluginRoot);
234
+ if (entry) {
235
+ plugins.push(entry);
239
236
  }
240
- const { plugin } = manifestToRegistry(manifest);
241
- plugins.push(plugin);
242
237
  }
243
238
  return {
244
- schema: "kb.studio/1",
245
- schemaVersion: 1,
239
+ schema: "kb.studio/2",
240
+ schemaVersion: 2,
246
241
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
247
242
  plugins
248
243
  };
249
244
  }
250
245
 
251
- export { combineManifestsToRegistry, loadRestApiConfig, manifestToRegistry, restApiConfigSchema };
246
+ export { combineManifestsToRegistry, loadRestApiConfig, manifestToRegistryEntry, restApiConfigSchema };
252
247
  //# sourceMappingURL=index.js.map
253
248
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config/schema.ts","../src/config/loader.ts","../src/studio-registry.ts"],"names":[],"mappings":";;;;;AAOO,IAAM,mBAAA,GAAsB,EAAE,MAAA,CAAO;AAAA,EAC1C,IAAA,EAAM,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,IAAI,CAAA;AAAA,EAC9C,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,SAAS,CAAA;AAAA,EACtC,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,OAAO,CAAA;AAAA,EACtC,IAAA,EAAM,EAAE,MAAA,CAAO;AAAA,IACb,OAAA,EAAS,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,MAAA,EAAQ,CAAA,CAAE,OAAA,CAAQ,CAAC,uBAAA,EAAyB,uBAAuB,CAAC,CAAA;AAAA,IACvF,gBAAA,EAAkB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI,CAAA;AAAA,IAC1C,OAAA,EAAS,CAAA,CAAE,IAAA,CAAK,CAAC,KAAA,EAAO,WAAW,MAAM,CAAC,CAAA,CAAE,OAAA,CAAQ,KAAK;AAAA,GAC1D,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EACb,QAAA,EAAU,EAAE,MAAA,CAAO;AAAA,IACjB,cAAA,EAAgB,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAK,CAAA;AAAA,IACzD,SAAA,EAAW,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,QAAU;AAAA,GAC1D,EAAE,QAAA,EAAS;AAAA,EACZ,SAAA,EAAW,EAAE,MAAA,CAAO;AAAA,IAClB,GAAA,EAAK,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,IAC3C,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,UAAU;AAAA,GAC1C,EAAE,QAAA,EAAS;AAAA,EACZ,OAAA,EAAS,EAAE,MAAA,CAAO;AAAA,IAChB,aAAA,EAAe,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,IACrD,UAAA,EAAY,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,GAAG,CAAA;AAAA,IACtD,SAAA,EAAW,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAI,CAAA;AAAA,IACnD,iBAAA,EAAmB,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC;AAAA,GACzD,EAAE,QAAA,EAAS;AAAA,EACZ,OAAA,EAAS,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EACvC,QAAA,EAAU,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA,EACnC,KAAA,EAAO,EACJ,MAAA,CAAO;AAAA,IACN,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,IACrB,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,IAAI;AAAA,GACnC,EACA,QAAA,EAAS;AAAA,EACZ,MAAA,EAAQ,EACL,MAAA,CAAO;AAAA,IACN,QAAA,EAAU,EACP,MAAA,CAAO;AAAA,MACN,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,MACvB,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,eAAe,CAAA;AAAA,MAC9C,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,cAAc;AAAA,KAC9C,EACA,QAAA;AAAS,GACb,EACA,QAAA,EAAS;AAAA,EACZ,KAAA,EAAO,EACJ,MAAA,CAAO;AAAA,IACN,OAAA,EAAS,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA,IAClC,UAAA,EAAY,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI;AAAA;AAAA,GACrC,EACA,QAAA,EAAS;AAAA,EACZ,GAAA,EAAK,EACF,MAAA,CAAO;AAAA,IACN,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,IACzB,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC;AAAA,GAC3B,EACA,QAAA;AACL,CAAC;;;ACjDD,eAAsB,iBAAA,CACpB,GAAA,GAAc,OAAA,CAAQ,GAAA,IACtB,YAAA,EAC0G;AAG1G,EAAA,IAAI,eAAoB,EAAC;AAEzB,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,UAAA,CAAW;AAAA,MAC9B,GAAA;AAAA,MACA,OAAA,EAAS,SAAA;AAAA;AAAA,MACT,SAAA,EAAW;AAAA,KACZ,CAAA;AACD,IAAA,YAAA,GAAe,MAAA,CAAO,MAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AAEN,IAAA,MAAM,EAAE,IAAA,EAAM,UAAA,EAAW,GAAI,MAAM,iBAAA,CAAkB;AAAA,MACnD,QAAA,EAAU,GAAA;AAAA,MACV,SAAA,EAAW,CAAC,oBAAA,EAAsB,oBAAoB;AAAA,KACvD,CAAA;AAED,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,MAAM,UAAA,GAAa,MAAM,uBAAA,CAA6B,UAAU,CAAA;AAChE,MAAA,IAAI,UAAA,CAAW,EAAA,IAAM,UAAA,CAAW,IAAA,EAAM,IAAA,EAAM;AAC1C,QAAA,YAAA,GAAe,WAAW,IAAA,CAAK,IAAA;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAA0B;AAAA,IAC9B,IAAA,EAAM,IAAA;AAAA,IACN,QAAA,EAAU,SAAA;AAAA,IACV,UAAA,EAAY,OAAA;AAAA,IACZ,IAAA,EAAM;AAAA,MACJ,OAAA,EAAS,CAAC,uBAAA,EAAyB,uBAAuB,CAAA;AAAA,MAC1D,gBAAA,EAAkB,IAAA;AAAA,MAClB,OAAA,EAAS;AAAA,KACX;AAAA,IACA,QAAA,EAAU;AAAA,MACR,cAAA,EAAgB,GAAA;AAAA,MAChB,SAAA,EAAW;AAAA,KACb;AAAA,IACA,SAAA,EAAW;AAAA;AAAA;AAAA,MAGT,GAAA,EAAK,GAAA;AAAA,MACL,UAAA,EAAY;AAAA,KACd;AAAA,IACA,SAAS,EAAC;AAAA,IACV,QAAA,EAAU;AAAA,GACZ;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,GAAA,KAAmD;AACpE,IAAA,MAAM,YAAoC,EAAC;AAE3C,IAAA,IAAI,IAAI,YAAA,EAAc;AACpB,MAAA,MAAM,UAAA,GAAa,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,cAAc,EAAE,CAAA;AACvD,MAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,UAAU,CAAA,EAAG;AAC7B,QAAA,SAAA,CAAU,IAAA,GAAO,UAAA;AAAA,MACnB;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,iBAAA,EAAmB;AACzB,MAAA,SAAA,CAAU,WAAW,GAAA,CAAI,iBAAA;AAAA,IAC3B;AAEA,IAAA,IAAI,IAAI,mBAAA,EAAqB;AAC3B,MAAA,SAAA,CAAU,aAAa,GAAA,CAAI,mBAAA;AAAA,IAC7B;AAEA,IAAA,IAAI,IAAI,oBAAA,EAAsB;AAC5B,MAAA,MAAM,OAAA,GAAU,GAAA,CAAI,oBAAA,CAAqB,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAA,MAAA,KAAU,MAAA,CAAO,IAAA,EAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AAC/F,MAAA,SAAA,CAAU,IAAA,GAAO;AAAA,QACf,GAAI,SAAA,CAAU,IAAA,IAAQ,QAAA,CAAS,IAAA;AAAA,QAC/B;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,oBAAA,EAAsB;AAC5B,MAAA,SAAA,CAAU,IAAA,GAAO;AAAA,QACf,GAAI,SAAA,CAAU,IAAA,IAAQ,QAAA,CAAS,IAAA;AAAA,QAC/B,SAAS,GAAA,CAAI;AAAA,OACf;AAAA,IACF;AAEA,IAAA,IAAI,GAAA,CAAI,iBAAA,KAAsB,MAAA,IAAU,GAAA,CAAI,sBAAsB,GAAA,EAAK;AACrE,MAAA,SAAA,CAAU,QAAA,GAAW,IAAA;AAAA,IACvB;AAEA,IAAA,IAAI,IAAI,iBAAA,EAAmB;AACzB,MAAA,SAAA,CAAU,KAAA,GAAQ;AAAA,QAChB,GAAI,SAAA,CAAU,KAAA,IAAS,EAAC;AAAA,QACxB,KAAK,GAAA,CAAI,iBAAA;AAAA,QACT,WAAW,SAAA,CAAU,KAAA,EAAO,SAAA,IAAa,QAAA,CAAS,OAAO,SAAA,IAAa;AAAA,OACxE;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,uBAAA,EAAyB;AAC/B,MAAA,SAAA,CAAU,KAAA,GAAQ;AAAA,QAChB,GAAI,SAAA,CAAU,KAAA,KAAU,GAAA,CAAI,iBAAA,GAAoB,EAAE,GAAA,EAAK,GAAA,CAAI,iBAAA,EAAkB,GAAI,QAAA,CAAS,KAAA,IAAS,EAAC,CAAA;AAAA,QACpG,WAAW,GAAA,CAAI;AAAA,OACjB;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,sBAAA,EAAwB;AAC9B,MAAA,MAAM,SAAA,GAAY,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,wBAAwB,EAAE,CAAA;AAChE,MAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,SAAS,CAAA,EAAG;AAC5B,QAAA,SAAA,CAAU,SAAA,GAAY;AAAA,UACpB,GAAI,UAAU,SAAA,IAAa,QAAA,CAAS,aAAa,EAAE,GAAA,EAAK,GAAA,EAAK,UAAA,EAAY,UAAA,EAAW;AAAA,UACpF,GAAA,EAAK;AAAA,SACP;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,yBAAA,EAA2B;AACjC,MAAA,SAAA,CAAU,SAAA,GAAY;AAAA,QACpB,GAAI,UAAU,SAAA,IAAa,QAAA,CAAS,aAAa,EAAE,GAAA,EAAK,GAAA,EAAK,UAAA,EAAY,UAAA,EAAW;AAAA,QACpF,YAAY,GAAA,CAAI;AAAA,OAClB;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,uBAAA,EAAyB;AAC/B,MAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,yBAAyB,EAAE,CAAA;AACrE,MAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,aAAa,CAAA,EAAG;AAChC,QAAA,MAAM,WAAA,GAAc,SAAA,CAAU,QAAA,IAAY,QAAA,CAAS,QAAA;AACnD,QAAA,SAAA,CAAU,QAAA,GAAW;AAAA,UACnB,cAAA,EAAgB,aAAA;AAAA,UAChB,SAAA,EAAW,WAAA,EAAa,SAAA,IAAa,QAAA,CAAS,UAAU,SAAA,IAAa;AAAA,SACvE;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,kBAAA,EAAoB;AAC1B,MAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,oBAAoB,EAAE,CAAA;AAClE,MAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,eAAe,CAAA,EAAG;AAClC,QAAA,MAAM,WAAA,GAAc,SAAA,CAAU,QAAA,IAAY,QAAA,CAAS,QAAA;AACnD,QAAA,SAAA,CAAU,QAAA,GAAW;AAAA,UACnB,cAAA,EAAgB,WAAA,EAAa,cAAA,IAAkB,QAAA,CAAS,UAAU,cAAA,IAAkB,GAAA;AAAA,UACpF,SAAA,EAAW;AAAA,SACb;AAAA,MACF;AAAA,IACF;AAEA,IAAA,OAAO,SAAA;AAAA,EACT,CAAA;AAGA,EAAA,MAAM,WAAW,aAAA,CAA6B;AAAA,IAC5C,QAAA;AAAA,IACA,UAAA,EAAY,YAAA;AAAA,IACZ,SAAA;AAAA,IACA,YAAA;AAAA,IACA,QAAA,EAAU,CAAC,GAAA,KAAQ;AACjB,MAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,SAAA,CAAU,GAAG,CAAA;AAChD,MAAA,IAAI,OAAO,OAAA,EAAS;AAClB,QAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AAAA,MACpB;AACA,MAAA,OAAO;AAAA,QACL,EAAA,EAAI,KAAA;AAAA,QACJ,aAAa,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,UAC7C,KAAA,EAAO,OAAA;AAAA,UACP,IAAA,EAAM,yBAAA;AAAA,UACN,OAAA,EAAS,GAAG,GAAA,CAAI,IAAA,CAAK,KAAK,GAAG,CAAC,CAAA,EAAA,EAAK,GAAA,CAAI,OAAO,CAAA;AAAA,SAChD,CAAE;AAAA,OACJ;AAAA,IACF;AAAA,GACD,CAAA;AAGD,EAAA,MAAM,SAAA,GAAY,mBAAA,CAAoB,KAAA,CAAM,QAAA,CAAS,KAAK,CAAA;AAE1D,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,SAAA;AAAA,IACR,aAAa,QAAA,CAAS;AAAA,GACxB;AACF;;;ACzKO,SAAS,mBAAmB,QAAA,EAKjC;AACA,EAAA,MAAM,SAAS,QAAA,CAAS,MAAA;AACxB,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ;AAAA,QACN,UAAU,QAAA,CAAS,EAAA;AAAA,QACnB,WAAA,EAAa,SAAS,OAAA,EAAS,IAAA;AAAA,QAC/B,SAAS,EAAC;AAAA,QACV,OAAO,EAAC;AAAA,QACR,SAAS;AAAC,OACZ;AAAA,MACA,SAAS,EAAC;AAAA,MACV,OAAO,EAAC;AAAA,MACR,SAAS;AAAC,KACZ;AAAA,EACF;AAGA,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,IAAW,EAAC;AACnC,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,IAAS,EAAC;AAC/B,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,IAAW,EAAC;AAEnC,EAAA,MAAM,MAAA,GAA4B;AAAA,IAChC,UAAU,QAAA,CAAS,EAAA;AAAA,IACnB,WAAA,EAAa,SAAS,OAAA,EAAS,IAAA;AAAA,IAC/B,OAAA;AAAA,IACA,KAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ;AAC3C;AAKO,SAAS,0BAAA,CACd,WACA,gBAAA,EACgB;AAChB,EAAA,MAAM,UAA+B,EAAC;AAEtC,EAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,IAAA,IAAI,CAAC,SAAS,MAAA,EAAQ;AAAC,MAAA;AAAA,IAAS;AAEhC,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,kBAAA,CAAmB,QAAQ,CAAA;AAC9C,IAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA,EACrB;AAEA,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,aAAA;AAAA,IACR,aAAA,EAAe,CAAA;AAAA,IACf,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,IACpC;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/rest-api-core/config/schema\n * Zod schema for REST API configuration\n */\n\nimport { z } from 'zod';\n\nexport const restApiConfigSchema = z.object({\n port: z.number().int().positive().default(5050),\n basePath: z.string().default('/api/v1'),\n apiVersion: z.string().default('1.0.0'),\n cors: z.object({\n origins: z.array(z.string()).default(['http://localhost:3000', 'http://localhost:5173']),\n allowCredentials: z.boolean().default(true),\n profile: z.enum(['dev', 'preview', 'prod']).default('dev'),\n }).default({}),\n timeouts: z.object({\n requestTimeout: z.number().int().positive().default(30000),\n bodyLimit: z.number().int().positive().default(10_485_760),\n }).optional(),\n rateLimit: z.object({\n max: z.number().int().positive().default(60),\n timeWindow: z.string().default('1 minute'),\n }).optional(),\n startup: z.object({\n maxConcurrent: z.number().int().positive().default(32),\n queueLimit: z.number().int().nonnegative().default(128),\n timeoutMs: z.number().int().positive().default(5000),\n retryAfterSeconds: z.number().int().positive().default(2),\n }).optional(),\n plugins: z.array(z.string()).default([]),\n mockMode: z.boolean().default(false),\n redis: z\n .object({\n url: z.string().min(1),\n namespace: z.string().default('kb'),\n })\n .optional(),\n events: z\n .object({\n registry: z\n .object({\n token: z.string().min(1),\n headerName: z.string().default('authorization'),\n queryParam: z.string().default('access_token'),\n })\n .optional(),\n })\n .optional(),\n http2: z\n .object({\n enabled: z.boolean().default(false),\n allowHTTP1: z.boolean().default(true), // Fallback for old clients\n })\n .optional(),\n ssl: z\n .object({\n keyPath: z.string().min(1),\n certPath: z.string().min(1),\n })\n .optional(),\n});\n\nexport type RestApiConfig = z.infer<typeof restApiConfigSchema>;\n\n","/**\n * @module @kb-labs/rest-api-core/config/loader\n * Configuration loader using loadBundle from @kb-labs/core-bundle\n */\n\nimport { loadBundle } from '@kb-labs/core-bundle';\nimport { resolveConfig, findNearestConfig, readJsonWithDiagnostics } from '@kb-labs/core-config';\nimport { restApiConfigSchema, type RestApiConfig } from './schema';\n\n/**\n * Load REST API configuration\n */\nexport async function loadRestApiConfig(\n cwd: string = process.cwd(),\n cliOverrides?: Partial<RestApiConfig>\n): Promise<{ config: RestApiConfig; diagnostics: Array<{ level: string; code: string; message: string }> }> {\n // Try to load bundle with 'restApi' product\n // Note: if 'restApi' is not registered as ProductId, we'll fall back to direct config loading\n let bundleConfig: any = {};\n \n try {\n const bundle = await loadBundle({\n cwd,\n product: 'restApi' as any, // TODO: Add 'restApi' to ProductId type\n profileId: 'default',\n });\n bundleConfig = bundle.config;\n } catch {\n // If bundle loading fails (product not registered), try direct config loading\n const { path: configPath } = await findNearestConfig({\n startDir: cwd,\n filenames: ['.kb/kb.config.json', '.kb/kb.config.yaml'],\n });\n \n if (configPath) {\n const readResult = await readJsonWithDiagnostics<any>(configPath);\n if (readResult.ok && readResult.data?.rest) {\n bundleConfig = readResult.data.rest;\n }\n }\n }\n \n const defaults: RestApiConfig = {\n port: 5050,\n basePath: '/api/v1',\n apiVersion: '1.0.0',\n cors: {\n origins: ['http://localhost:3000', 'http://localhost:5173'],\n allowCredentials: true,\n profile: 'dev',\n },\n timeouts: {\n requestTimeout: 30000,\n bodyLimit: 10_485_760,\n },\n rateLimit: {\n // 600/min for dev (dashboard needs ~10 requests per page load)\n // Override with KB_REST_RATE_LIMIT_MAX=60 for production public API\n max: 600,\n timeWindow: '1 minute',\n },\n plugins: [],\n mockMode: false,\n };\n\n const envMapper = (env: NodeJS.ProcessEnv): Partial<RestApiConfig> => {\n const overrides: Partial<RestApiConfig> = {};\n\n if (env.KB_REST_PORT) {\n const parsedPort = Number.parseInt(env.KB_REST_PORT, 10);\n if (!Number.isNaN(parsedPort)) {\n overrides.port = parsedPort;\n }\n }\n\n if (env.KB_REST_BASE_PATH) {\n overrides.basePath = env.KB_REST_BASE_PATH;\n }\n\n if (env.KB_REST_API_VERSION) {\n overrides.apiVersion = env.KB_REST_API_VERSION;\n }\n\n if (env.KB_REST_CORS_ORIGINS) {\n const origins = env.KB_REST_CORS_ORIGINS.split(',').map(origin => origin.trim()).filter(Boolean);\n overrides.cors = {\n ...(overrides.cors ?? defaults.cors),\n origins,\n };\n }\n\n if (env.KB_REST_CORS_PROFILE) {\n overrides.cors = {\n ...(overrides.cors ?? defaults.cors),\n profile: env.KB_REST_CORS_PROFILE as RestApiConfig['cors']['profile'],\n };\n }\n\n if (env.KB_REST_MOCK_MODE === 'true' || env.KB_REST_MOCK_MODE === '1') {\n overrides.mockMode = true;\n }\n\n if (env.KB_REST_REDIS_URL) {\n overrides.redis = {\n ...(overrides.redis ?? {}),\n url: env.KB_REST_REDIS_URL,\n namespace: overrides.redis?.namespace ?? defaults.redis?.namespace ?? 'kb',\n } as RestApiConfig['redis'];\n }\n\n if (env.KB_REST_REDIS_NAMESPACE) {\n overrides.redis = {\n ...(overrides.redis ?? (env.KB_REST_REDIS_URL ? { url: env.KB_REST_REDIS_URL } : defaults.redis ?? {})),\n namespace: env.KB_REST_REDIS_NAMESPACE,\n } as RestApiConfig['redis'];\n }\n\n if (env.KB_REST_RATE_LIMIT_MAX) {\n const parsedMax = Number.parseInt(env.KB_REST_RATE_LIMIT_MAX, 10);\n if (!Number.isNaN(parsedMax)) {\n overrides.rateLimit = {\n ...(overrides.rateLimit ?? defaults.rateLimit ?? { max: 600, timeWindow: '1 minute' }),\n max: parsedMax,\n };\n }\n }\n\n if (env.KB_REST_RATE_LIMIT_WINDOW) {\n overrides.rateLimit = {\n ...(overrides.rateLimit ?? defaults.rateLimit ?? { max: 600, timeWindow: '1 minute' }),\n timeWindow: env.KB_REST_RATE_LIMIT_WINDOW,\n };\n }\n\n if (env.KB_REST_REQUEST_TIMEOUT) {\n const parsedTimeout = Number.parseInt(env.KB_REST_REQUEST_TIMEOUT, 10);\n if (!Number.isNaN(parsedTimeout)) {\n const baseTimeout = overrides.timeouts ?? defaults.timeouts;\n overrides.timeouts = {\n requestTimeout: parsedTimeout,\n bodyLimit: baseTimeout?.bodyLimit ?? defaults.timeouts?.bodyLimit ?? 10_485_760,\n };\n }\n }\n\n if (env.KB_REST_BODY_LIMIT) {\n const parsedBodyLimit = Number.parseInt(env.KB_REST_BODY_LIMIT, 10);\n if (!Number.isNaN(parsedBodyLimit)) {\n const baseTimeout = overrides.timeouts ?? defaults.timeouts;\n overrides.timeouts = {\n requestTimeout: baseTimeout?.requestTimeout ?? defaults.timeouts?.requestTimeout ?? 30000,\n bodyLimit: parsedBodyLimit,\n };\n }\n }\n\n return overrides;\n };\n \n // Resolve configuration: defaults → bundleConfig → env → cliOverrides\n const resolved = resolveConfig<RestApiConfig>({\n defaults,\n fileConfig: bundleConfig,\n envMapper,\n cliOverrides,\n validate: (cfg) => {\n const result = restApiConfigSchema.safeParse(cfg);\n if (result.success) {\n return { ok: true };\n }\n return {\n ok: false,\n diagnostics: result.error.errors.map((err) => ({\n level: 'error' as const,\n code: 'CONFIG_VALIDATION_ERROR',\n message: `${err.path.join('.')}: ${err.message}`,\n })),\n };\n },\n });\n \n // Parse with Zod to get properly typed config\n const validated = restApiConfigSchema.parse(resolved.value);\n \n return {\n config: validated,\n diagnostics: resolved.diagnostics,\n };\n}\n\n","/**\n * @module @kb-labs/rest-api-core/studio-registry\n *\n * Transforms ManifestV3 studio sections into StudioRegistry format.\n * REST API uses this to provide pre-computed registry data to Studio frontend.\n */\n\nimport type { ManifestV3 } from '@kb-labs/plugin-contracts';\nimport type {\n StudioRegistry,\n StudioPluginEntry,\n StudioWidgetDecl,\n StudioMenuDecl,\n StudioLayoutDecl,\n} from '@kb-labs/rest-api-contracts';\n\n/**\n * Convert a single ManifestV3 to StudioRegistry entries.\n */\nexport function manifestToRegistry(manifest: ManifestV3): {\n plugin: StudioPluginEntry;\n widgets: StudioWidgetDecl[];\n menus: StudioMenuDecl[];\n layouts: StudioLayoutDecl[];\n} {\n const studio = manifest.studio;\n if (!studio) {\n return {\n plugin: {\n pluginId: manifest.id,\n displayName: manifest.display?.name,\n widgets: [],\n menus: [],\n layouts: [],\n },\n widgets: [],\n menus: [],\n layouts: [],\n };\n }\n\n // Widgets, menus, layouts are already in correct format from manifest.studio\n const widgets = studio.widgets ?? [];\n const menus = studio.menus ?? [];\n const layouts = studio.layouts ?? [];\n\n const plugin: StudioPluginEntry = {\n pluginId: manifest.id,\n displayName: manifest.display?.name,\n widgets,\n menus,\n layouts,\n };\n\n return { plugin, widgets, menus, layouts };\n}\n\n/**\n * Combine multiple manifests into a single StudioRegistry.\n */\nexport function combineManifestsToRegistry(\n manifests: ManifestV3[],\n _registryVersion?: string\n): StudioRegistry {\n const plugins: StudioPluginEntry[] = [];\n\n for (const manifest of manifests) {\n if (!manifest.studio) {continue;}\n\n const { plugin } = manifestToRegistry(manifest);\n plugins.push(plugin);\n }\n\n return {\n schema: 'kb.studio/1',\n schemaVersion: 1,\n generatedAt: new Date().toISOString(),\n plugins,\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/config/schema.ts","../src/config/loader.ts","../src/studio-registry.ts"],"names":[],"mappings":";;;;;;;AAOO,IAAM,mBAAA,GAAsB,EAAE,MAAA,CAAO;AAAA,EAC1C,IAAA,EAAM,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,IAAI,CAAA;AAAA,EAC9C,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,SAAS,CAAA;AAAA,EACtC,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,OAAO,CAAA;AAAA,EACtC,IAAA,EAAM,EAAE,MAAA,CAAO;AAAA,IACb,OAAA,EAAS,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,MAAA,EAAQ,CAAA,CAAE,OAAA,CAAQ,CAAC,uBAAA,EAAyB,uBAAuB,CAAC,CAAA;AAAA,IACvF,gBAAA,EAAkB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI,CAAA;AAAA,IAC1C,OAAA,EAAS,CAAA,CAAE,IAAA,CAAK,CAAC,KAAA,EAAO,WAAW,MAAM,CAAC,CAAA,CAAE,OAAA,CAAQ,KAAK;AAAA,GAC1D,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EACb,QAAA,EAAU,EAAE,MAAA,CAAO;AAAA,IACjB,cAAA,EAAgB,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAK,CAAA;AAAA,IACzD,SAAA,EAAW,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,QAAU;AAAA,GAC1D,EAAE,QAAA,EAAS;AAAA,EACZ,SAAA,EAAW,EAAE,MAAA,CAAO;AAAA,IAClB,GAAA,EAAK,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,IAC3C,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,UAAU;AAAA,GAC1C,EAAE,QAAA,EAAS;AAAA,EACZ,OAAA,EAAS,EAAE,MAAA,CAAO;AAAA,IAChB,aAAA,EAAe,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,IACrD,UAAA,EAAY,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,GAAG,CAAA;AAAA,IACtD,SAAA,EAAW,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAI,CAAA;AAAA,IACnD,iBAAA,EAAmB,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC;AAAA,GACzD,EAAE,QAAA,EAAS;AAAA,EACZ,OAAA,EAAS,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EACvC,QAAA,EAAU,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA,EACnC,KAAA,EAAO,EACJ,MAAA,CAAO;AAAA,IACN,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,IACrB,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,IAAI;AAAA,GACnC,EACA,QAAA,EAAS;AAAA,EACZ,MAAA,EAAQ,EACL,MAAA,CAAO;AAAA,IACN,QAAA,EAAU,EACP,MAAA,CAAO;AAAA,MACN,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,MACvB,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,eAAe,CAAA;AAAA,MAC9C,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,cAAc;AAAA,KAC9C,EACA,QAAA;AAAS,GACb,EACA,QAAA,EAAS;AAAA,EACZ,KAAA,EAAO,EACJ,MAAA,CAAO;AAAA,IACN,OAAA,EAAS,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA,IAClC,UAAA,EAAY,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI;AAAA;AAAA,GACrC,EACA,QAAA,EAAS;AAAA,EACZ,GAAA,EAAK,EACF,MAAA,CAAO;AAAA,IACN,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,IACzB,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC;AAAA,GAC3B,EACA,QAAA;AACL,CAAC;;;ACjDD,eAAsB,iBAAA,CACpB,GAAA,GAAc,OAAA,CAAQ,GAAA,IACtB,YAAA,EAC0G;AAG1G,EAAA,IAAI,eAAoB,EAAC;AAEzB,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,UAAA,CAAW;AAAA,MAC9B,GAAA;AAAA,MACA,OAAA,EAAS,SAAA;AAAA;AAAA,MACT,SAAA,EAAW;AAAA,KACZ,CAAA;AACD,IAAA,YAAA,GAAe,MAAA,CAAO,MAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AAEN,IAAA,MAAM,EAAE,IAAA,EAAM,UAAA,EAAW,GAAI,MAAM,iBAAA,CAAkB;AAAA,MACnD,QAAA,EAAU,GAAA;AAAA,MACV,SAAA,EAAW,CAAC,oBAAA,EAAsB,oBAAoB;AAAA,KACvD,CAAA;AAED,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,MAAM,UAAA,GAAa,MAAM,uBAAA,CAA6B,UAAU,CAAA;AAChE,MAAA,IAAI,UAAA,CAAW,EAAA,IAAM,UAAA,CAAW,IAAA,EAAM,IAAA,EAAM;AAC1C,QAAA,YAAA,GAAe,WAAW,IAAA,CAAK,IAAA;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAA0B;AAAA,IAC9B,IAAA,EAAM,IAAA;AAAA,IACN,QAAA,EAAU,SAAA;AAAA,IACV,UAAA,EAAY,OAAA;AAAA,IACZ,IAAA,EAAM;AAAA,MACJ,OAAA,EAAS,CAAC,uBAAA,EAAyB,uBAAuB,CAAA;AAAA,MAC1D,gBAAA,EAAkB,IAAA;AAAA,MAClB,OAAA,EAAS;AAAA,KACX;AAAA,IACA,QAAA,EAAU;AAAA,MACR,cAAA,EAAgB,GAAA;AAAA,MAChB,SAAA,EAAW;AAAA,KACb;AAAA,IACA,SAAA,EAAW;AAAA;AAAA;AAAA,MAGT,GAAA,EAAK,GAAA;AAAA,MACL,UAAA,EAAY;AAAA,KACd;AAAA,IACA,SAAS,EAAC;AAAA,IACV,QAAA,EAAU;AAAA,GACZ;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,GAAA,KAAmD;AACpE,IAAA,MAAM,YAAoC,EAAC;AAE3C,IAAA,IAAI,IAAI,YAAA,EAAc;AACpB,MAAA,MAAM,UAAA,GAAa,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,cAAc,EAAE,CAAA;AACvD,MAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,UAAU,CAAA,EAAG;AAC7B,QAAA,SAAA,CAAU,IAAA,GAAO,UAAA;AAAA,MACnB;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,iBAAA,EAAmB;AACzB,MAAA,SAAA,CAAU,WAAW,GAAA,CAAI,iBAAA;AAAA,IAC3B;AAEA,IAAA,IAAI,IAAI,mBAAA,EAAqB;AAC3B,MAAA,SAAA,CAAU,aAAa,GAAA,CAAI,mBAAA;AAAA,IAC7B;AAEA,IAAA,IAAI,IAAI,oBAAA,EAAsB;AAC5B,MAAA,MAAM,OAAA,GAAU,GAAA,CAAI,oBAAA,CAAqB,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,CAAA,MAAA,KAAU,MAAA,CAAO,IAAA,EAAM,CAAA,CAAE,OAAO,OAAO,CAAA;AAC/F,MAAA,SAAA,CAAU,IAAA,GAAO;AAAA,QACf,GAAI,SAAA,CAAU,IAAA,IAAQ,QAAA,CAAS,IAAA;AAAA,QAC/B;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,oBAAA,EAAsB;AAC5B,MAAA,SAAA,CAAU,IAAA,GAAO;AAAA,QACf,GAAI,SAAA,CAAU,IAAA,IAAQ,QAAA,CAAS,IAAA;AAAA,QAC/B,SAAS,GAAA,CAAI;AAAA,OACf;AAAA,IACF;AAEA,IAAA,IAAI,GAAA,CAAI,iBAAA,KAAsB,MAAA,IAAU,GAAA,CAAI,sBAAsB,GAAA,EAAK;AACrE,MAAA,SAAA,CAAU,QAAA,GAAW,IAAA;AAAA,IACvB;AAEA,IAAA,IAAI,IAAI,iBAAA,EAAmB;AACzB,MAAA,SAAA,CAAU,KAAA,GAAQ;AAAA,QAChB,GAAI,SAAA,CAAU,KAAA,IAAS,EAAC;AAAA,QACxB,KAAK,GAAA,CAAI,iBAAA;AAAA,QACT,WAAW,SAAA,CAAU,KAAA,EAAO,SAAA,IAAa,QAAA,CAAS,OAAO,SAAA,IAAa;AAAA,OACxE;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,uBAAA,EAAyB;AAC/B,MAAA,SAAA,CAAU,KAAA,GAAQ;AAAA,QAChB,GAAI,SAAA,CAAU,KAAA,KAAU,GAAA,CAAI,iBAAA,GAAoB,EAAE,GAAA,EAAK,GAAA,CAAI,iBAAA,EAAkB,GAAI,QAAA,CAAS,KAAA,IAAS,EAAC,CAAA;AAAA,QACpG,WAAW,GAAA,CAAI;AAAA,OACjB;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,sBAAA,EAAwB;AAC9B,MAAA,MAAM,SAAA,GAAY,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,wBAAwB,EAAE,CAAA;AAChE,MAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,SAAS,CAAA,EAAG;AAC5B,QAAA,SAAA,CAAU,SAAA,GAAY;AAAA,UACpB,GAAI,UAAU,SAAA,IAAa,QAAA,CAAS,aAAa,EAAE,GAAA,EAAK,GAAA,EAAK,UAAA,EAAY,UAAA,EAAW;AAAA,UACpF,GAAA,EAAK;AAAA,SACP;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,yBAAA,EAA2B;AACjC,MAAA,SAAA,CAAU,SAAA,GAAY;AAAA,QACpB,GAAI,UAAU,SAAA,IAAa,QAAA,CAAS,aAAa,EAAE,GAAA,EAAK,GAAA,EAAK,UAAA,EAAY,UAAA,EAAW;AAAA,QACpF,YAAY,GAAA,CAAI;AAAA,OAClB;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,uBAAA,EAAyB;AAC/B,MAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,yBAAyB,EAAE,CAAA;AACrE,MAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,aAAa,CAAA,EAAG;AAChC,QAAA,MAAM,WAAA,GAAc,SAAA,CAAU,QAAA,IAAY,QAAA,CAAS,QAAA;AACnD,QAAA,SAAA,CAAU,QAAA,GAAW;AAAA,UACnB,cAAA,EAAgB,aAAA;AAAA,UAChB,SAAA,EAAW,WAAA,EAAa,SAAA,IAAa,QAAA,CAAS,UAAU,SAAA,IAAa;AAAA,SACvE;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,IAAI,kBAAA,EAAoB;AAC1B,MAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,oBAAoB,EAAE,CAAA;AAClE,MAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,eAAe,CAAA,EAAG;AAClC,QAAA,MAAM,WAAA,GAAc,SAAA,CAAU,QAAA,IAAY,QAAA,CAAS,QAAA;AACnD,QAAA,SAAA,CAAU,QAAA,GAAW;AAAA,UACnB,cAAA,EAAgB,WAAA,EAAa,cAAA,IAAkB,QAAA,CAAS,UAAU,cAAA,IAAkB,GAAA;AAAA,UACpF,SAAA,EAAW;AAAA,SACb;AAAA,MACF;AAAA,IACF;AAEA,IAAA,OAAO,SAAA;AAAA,EACT,CAAA;AAGA,EAAA,MAAM,WAAW,aAAA,CAA6B;AAAA,IAC5C,QAAA;AAAA,IACA,UAAA,EAAY,YAAA;AAAA,IACZ,SAAA;AAAA,IACA,YAAA;AAAA,IACA,QAAA,EAAU,CAAC,GAAA,KAAQ;AACjB,MAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,SAAA,CAAU,GAAG,CAAA;AAChD,MAAA,IAAI,OAAO,OAAA,EAAS;AAClB,QAAA,OAAO,EAAE,IAAI,IAAA,EAAK;AAAA,MACpB;AACA,MAAA,OAAO;AAAA,QACL,EAAA,EAAI,KAAA;AAAA,QACJ,aAAa,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,UAC7C,KAAA,EAAO,OAAA;AAAA,UACP,IAAA,EAAM,yBAAA;AAAA,UACN,OAAA,EAAS,GAAG,GAAA,CAAI,IAAA,CAAK,KAAK,GAAG,CAAC,CAAA,EAAA,EAAK,GAAA,CAAI,OAAO,CAAA;AAAA,SAChD,CAAE;AAAA,OACJ;AAAA,IACF;AAAA,GACD,CAAA;AAGD,EAAA,MAAM,SAAA,GAAY,mBAAA,CAAoB,KAAA,CAAM,QAAA,CAAS,KAAK,CAAA;AAE1D,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,SAAA;AAAA,IACR,aAAa,QAAA,CAAS;AAAA,GACxB;AACF;AC7KA,SAAS,sBAAsB,eAAA,EAAiC;AAC9D,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,IAAA,CAAK,eAAA,EAAiB,gBAAgB,CAAC,CAAA;AAC7D,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,OAAO,CAAC,CAAA;AAAA,EACxC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,GAAA;AAAA,EACT;AACF;AAQO,SAAS,uBAAA,CACd,UACA,UAAA,EAC4B;AAC5B,EAAA,MAAM,SAAS,QAAA,CAAS,MAAA;AACxB,EAAA,IAAI,CAAC,MAAA,IAAU,MAAA,CAAO,OAAA,KAAY,CAAA,EAAG;AACnC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,UAAU,QAAA,CAAS,EAAA;AAAA,IACnB,WAAA,EAAa,SAAS,OAAA,EAAS,IAAA;AAAA,IAC/B,eAAe,QAAA,CAAS,OAAA;AAAA,IACxB,YAAY,MAAA,CAAO,UAAA;AAAA,IACnB,cAAA,EAAgB,CAAA,SAAA,EAAY,QAAA,CAAS,EAAE,CAAA,0BAAA,EAA6B,qBAAA,CAAsB,IAAA,CAAK,UAAA,EAAY,MAAA,EAAQ,SAAS,CAAC,CAAC,CAAA,CAAA;AAAA,IAC9H,eAAA,EAAiB,IAAA,CAAK,UAAA,EAAY,MAAA,EAAQ,SAAS,CAAA;AAAA,IACnD,KAAA,EAAO,MAAA,CAAO,KAAA,IAAS,EAAC;AAAA,IACxB,KAAA,EAAO,MAAA,CAAO,KAAA,IAAS;AAAC,GAC1B;AACF;AAKO,SAAS,2BACd,SAAA,EACkB;AAClB,EAAA,MAAM,UAAiC,EAAC;AAExC,EAAA,KAAA,MAAW,EAAE,QAAA,EAAU,UAAA,EAAW,IAAK,SAAA,EAAW;AAChD,IAAA,MAAM,KAAA,GAAQ,uBAAA,CAAwB,QAAA,EAAU,UAAU,CAAA;AAC1D,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA,IACpB;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,aAAA;AAAA,IACR,aAAA,EAAe,CAAA;AAAA,IACf,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,IACpC;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/rest-api-core/config/schema\n * Zod schema for REST API configuration\n */\n\nimport { z } from 'zod';\n\nexport const restApiConfigSchema = z.object({\n port: z.number().int().positive().default(5050),\n basePath: z.string().default('/api/v1'),\n apiVersion: z.string().default('1.0.0'),\n cors: z.object({\n origins: z.array(z.string()).default(['http://localhost:3000', 'http://localhost:5173']),\n allowCredentials: z.boolean().default(true),\n profile: z.enum(['dev', 'preview', 'prod']).default('dev'),\n }).default({}),\n timeouts: z.object({\n requestTimeout: z.number().int().positive().default(30000),\n bodyLimit: z.number().int().positive().default(10_485_760),\n }).optional(),\n rateLimit: z.object({\n max: z.number().int().positive().default(60),\n timeWindow: z.string().default('1 minute'),\n }).optional(),\n startup: z.object({\n maxConcurrent: z.number().int().positive().default(32),\n queueLimit: z.number().int().nonnegative().default(128),\n timeoutMs: z.number().int().positive().default(5000),\n retryAfterSeconds: z.number().int().positive().default(2),\n }).optional(),\n plugins: z.array(z.string()).default([]),\n mockMode: z.boolean().default(false),\n redis: z\n .object({\n url: z.string().min(1),\n namespace: z.string().default('kb'),\n })\n .optional(),\n events: z\n .object({\n registry: z\n .object({\n token: z.string().min(1),\n headerName: z.string().default('authorization'),\n queryParam: z.string().default('access_token'),\n })\n .optional(),\n })\n .optional(),\n http2: z\n .object({\n enabled: z.boolean().default(false),\n allowHTTP1: z.boolean().default(true), // Fallback for old clients\n })\n .optional(),\n ssl: z\n .object({\n keyPath: z.string().min(1),\n certPath: z.string().min(1),\n })\n .optional(),\n});\n\nexport type RestApiConfig = z.infer<typeof restApiConfigSchema>;\n\n","/**\n * @module @kb-labs/rest-api-core/config/loader\n * Configuration loader using loadBundle from @kb-labs/core-bundle\n */\n\nimport { loadBundle } from '@kb-labs/core-bundle';\nimport { resolveConfig, findNearestConfig, readJsonWithDiagnostics } from '@kb-labs/core-config';\nimport { restApiConfigSchema, type RestApiConfig } from './schema';\n\n/**\n * Load REST API configuration\n */\nexport async function loadRestApiConfig(\n cwd: string = process.cwd(),\n cliOverrides?: Partial<RestApiConfig>\n): Promise<{ config: RestApiConfig; diagnostics: Array<{ level: string; code: string; message: string }> }> {\n // Try to load bundle with 'restApi' product\n // Note: if 'restApi' is not registered as ProductId, we'll fall back to direct config loading\n let bundleConfig: any = {};\n \n try {\n const bundle = await loadBundle({\n cwd,\n product: 'restApi' as any, // TODO: Add 'restApi' to ProductId type\n profileId: 'default',\n });\n bundleConfig = bundle.config;\n } catch {\n // If bundle loading fails (product not registered), try direct config loading\n const { path: configPath } = await findNearestConfig({\n startDir: cwd,\n filenames: ['.kb/kb.config.json', '.kb/kb.config.yaml'],\n });\n \n if (configPath) {\n const readResult = await readJsonWithDiagnostics<any>(configPath);\n if (readResult.ok && readResult.data?.rest) {\n bundleConfig = readResult.data.rest;\n }\n }\n }\n \n const defaults: RestApiConfig = {\n port: 5050,\n basePath: '/api/v1',\n apiVersion: '1.0.0',\n cors: {\n origins: ['http://localhost:3000', 'http://localhost:5173'],\n allowCredentials: true,\n profile: 'dev',\n },\n timeouts: {\n requestTimeout: 30000,\n bodyLimit: 10_485_760,\n },\n rateLimit: {\n // 600/min for dev (dashboard needs ~10 requests per page load)\n // Override with KB_REST_RATE_LIMIT_MAX=60 for production public API\n max: 600,\n timeWindow: '1 minute',\n },\n plugins: [],\n mockMode: false,\n };\n\n const envMapper = (env: NodeJS.ProcessEnv): Partial<RestApiConfig> => {\n const overrides: Partial<RestApiConfig> = {};\n\n if (env.KB_REST_PORT) {\n const parsedPort = Number.parseInt(env.KB_REST_PORT, 10);\n if (!Number.isNaN(parsedPort)) {\n overrides.port = parsedPort;\n }\n }\n\n if (env.KB_REST_BASE_PATH) {\n overrides.basePath = env.KB_REST_BASE_PATH;\n }\n\n if (env.KB_REST_API_VERSION) {\n overrides.apiVersion = env.KB_REST_API_VERSION;\n }\n\n if (env.KB_REST_CORS_ORIGINS) {\n const origins = env.KB_REST_CORS_ORIGINS.split(',').map(origin => origin.trim()).filter(Boolean);\n overrides.cors = {\n ...(overrides.cors ?? defaults.cors),\n origins,\n };\n }\n\n if (env.KB_REST_CORS_PROFILE) {\n overrides.cors = {\n ...(overrides.cors ?? defaults.cors),\n profile: env.KB_REST_CORS_PROFILE as RestApiConfig['cors']['profile'],\n };\n }\n\n if (env.KB_REST_MOCK_MODE === 'true' || env.KB_REST_MOCK_MODE === '1') {\n overrides.mockMode = true;\n }\n\n if (env.KB_REST_REDIS_URL) {\n overrides.redis = {\n ...(overrides.redis ?? {}),\n url: env.KB_REST_REDIS_URL,\n namespace: overrides.redis?.namespace ?? defaults.redis?.namespace ?? 'kb',\n } as RestApiConfig['redis'];\n }\n\n if (env.KB_REST_REDIS_NAMESPACE) {\n overrides.redis = {\n ...(overrides.redis ?? (env.KB_REST_REDIS_URL ? { url: env.KB_REST_REDIS_URL } : defaults.redis ?? {})),\n namespace: env.KB_REST_REDIS_NAMESPACE,\n } as RestApiConfig['redis'];\n }\n\n if (env.KB_REST_RATE_LIMIT_MAX) {\n const parsedMax = Number.parseInt(env.KB_REST_RATE_LIMIT_MAX, 10);\n if (!Number.isNaN(parsedMax)) {\n overrides.rateLimit = {\n ...(overrides.rateLimit ?? defaults.rateLimit ?? { max: 600, timeWindow: '1 minute' }),\n max: parsedMax,\n };\n }\n }\n\n if (env.KB_REST_RATE_LIMIT_WINDOW) {\n overrides.rateLimit = {\n ...(overrides.rateLimit ?? defaults.rateLimit ?? { max: 600, timeWindow: '1 minute' }),\n timeWindow: env.KB_REST_RATE_LIMIT_WINDOW,\n };\n }\n\n if (env.KB_REST_REQUEST_TIMEOUT) {\n const parsedTimeout = Number.parseInt(env.KB_REST_REQUEST_TIMEOUT, 10);\n if (!Number.isNaN(parsedTimeout)) {\n const baseTimeout = overrides.timeouts ?? defaults.timeouts;\n overrides.timeouts = {\n requestTimeout: parsedTimeout,\n bodyLimit: baseTimeout?.bodyLimit ?? defaults.timeouts?.bodyLimit ?? 10_485_760,\n };\n }\n }\n\n if (env.KB_REST_BODY_LIMIT) {\n const parsedBodyLimit = Number.parseInt(env.KB_REST_BODY_LIMIT, 10);\n if (!Number.isNaN(parsedBodyLimit)) {\n const baseTimeout = overrides.timeouts ?? defaults.timeouts;\n overrides.timeouts = {\n requestTimeout: baseTimeout?.requestTimeout ?? defaults.timeouts?.requestTimeout ?? 30000,\n bodyLimit: parsedBodyLimit,\n };\n }\n }\n\n return overrides;\n };\n \n // Resolve configuration: defaults → bundleConfig → env → cliOverrides\n const resolved = resolveConfig<RestApiConfig>({\n defaults,\n fileConfig: bundleConfig,\n envMapper,\n cliOverrides,\n validate: (cfg) => {\n const result = restApiConfigSchema.safeParse(cfg);\n if (result.success) {\n return { ok: true };\n }\n return {\n ok: false,\n diagnostics: result.error.errors.map((err) => ({\n level: 'error' as const,\n code: 'CONFIG_VALIDATION_ERROR',\n message: `${err.path.join('.')}: ${err.message}`,\n })),\n };\n },\n });\n \n // Parse with Zod to get properly typed config\n const validated = restApiConfigSchema.parse(resolved.value);\n \n return {\n config: validated,\n diagnostics: resolved.diagnostics,\n };\n}\n\n","/**\n * @module @kb-labs/rest-api-core/studio-registry\n *\n * Transforms ManifestV3 studio sections into StudioRegistryV2 format.\n *\n * Each plugin entry includes:\n * - remoteEntryUrl: browser-facing URL (proxied through gateway)\n * - widgetBundleDir: absolute filesystem path to dist/widgets/ (used by gateway to serve files)\n */\n\nimport { join } from 'node:path';\nimport { statSync } from 'node:fs';\nimport type { ManifestV3 } from '@kb-labs/plugin-contracts';\nimport type { StudioRegistryV2, StudioPluginEntryV2 } from '@kb-labs/rest-api-contracts';\n\nfunction getRemoteEntryVersion(widgetBundleDir: string): string {\n try {\n const stat = statSync(join(widgetBundleDir, 'remoteEntry.js'));\n return String(Math.floor(stat.mtimeMs));\n } catch {\n return '0';\n }\n}\n\n/**\n * Convert a single ManifestV3 with studio V2 config to a registry entry.\n *\n * @param manifest - Plugin manifest with studio V2 config\n * @param pluginRoot - Absolute path to the plugin package root (where package.json lives)\n */\nexport function manifestToRegistryEntry(\n manifest: ManifestV3,\n pluginRoot: string,\n): StudioPluginEntryV2 | null {\n const studio = manifest.studio;\n if (!studio || studio.version !== 2) {\n return null;\n }\n\n return {\n pluginId: manifest.id,\n displayName: manifest.display?.name,\n pluginVersion: manifest.version,\n remoteName: studio.remoteName,\n remoteEntryUrl: `/plugins/${manifest.id}/widgets/remoteEntry.js?v=${getRemoteEntryVersion(join(pluginRoot, 'dist', 'widgets'))}`,\n widgetBundleDir: join(pluginRoot, 'dist', 'widgets'),\n pages: studio.pages ?? [],\n menus: studio.menus ?? [],\n };\n}\n\n/**\n * Combine multiple manifests into a StudioRegistryV2.\n */\nexport function combineManifestsToRegistry(\n manifests: Array<{ manifest: ManifestV3; pluginRoot: string }>,\n): StudioRegistryV2 {\n const plugins: StudioPluginEntryV2[] = [];\n\n for (const { manifest, pluginRoot } of manifests) {\n const entry = manifestToRegistryEntry(manifest, pluginRoot);\n if (entry) {\n plugins.push(entry);\n }\n }\n\n return {\n schema: 'kb.studio/2',\n schemaVersion: 2,\n generatedAt: new Date().toISOString(),\n plugins,\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kb-labs/rest-api-core",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Core REST API functionality for KB Labs, including request handling and routing",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,16 +17,26 @@
17
17
  "LICENSE"
18
18
  ],
19
19
  "sideEffects": false,
20
+ "scripts": {
21
+ "clean": "rimraf dist",
22
+ "build": "tsup",
23
+ "dev": "tsup --watch",
24
+ "type-check": "tsc --noEmit",
25
+ "lint": "eslint .",
26
+ "lint:fix": "eslint . --fix",
27
+ "test": "vitest run --passWithNoTests -c ../../vitest.config.ts",
28
+ "test:watch": "vitest --passWithNoTests -c ../../vitest.config.ts"
29
+ },
20
30
  "dependencies": {
21
31
  "zod": "^3.23.8",
22
- "@kb-labs/core-bundle": "link:../../../kb-labs-core/packages/core-bundle",
23
- "@kb-labs/core-config": "link:../../../kb-labs-core/packages/core-config",
24
- "@kb-labs/plugin-contracts": "link:../../../kb-labs-plugin/packages/plugin-contracts",
25
- "@kb-labs/rest-api-contracts": "link:../rest-api-contracts"
32
+ "@kb-labs/core-bundle": "^1.4.0",
33
+ "@kb-labs/core-config": "^1.4.0",
34
+ "@kb-labs/plugin-contracts": "^1.2.0",
35
+ "@kb-labs/rest-api-contracts": "^1.2.0"
26
36
  },
27
37
  "devDependencies": {
28
38
  "vitest": "^3.2.4",
29
- "@kb-labs/devkit": "link:../../../kb-labs-devkit",
39
+ "@kb-labs/devkit": "link:../../../../infra/kb-labs-devkit",
30
40
  "tsup": "^8.5.0",
31
41
  "@types/node": "^24.3.3",
32
42
  "rimraf": "^6.0.1",
@@ -38,15 +48,5 @@
38
48
  },
39
49
  "publishConfig": {
40
50
  "access": "public"
41
- },
42
- "scripts": {
43
- "clean": "rimraf dist",
44
- "build": "tsup",
45
- "dev": "tsup --watch",
46
- "type-check": "tsc --noEmit",
47
- "lint": "eslint .",
48
- "lint:fix": "eslint . --fix",
49
- "test": "vitest run --passWithNoTests -c ../../vitest.config.ts",
50
- "test:watch": "vitest --passWithNoTests -c ../../vitest.config.ts"
51
51
  }
52
- }
52
+ }