@happyvertical/smrt-core 0.38.25 → 0.38.26

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "timestamp": 1783646383847,
3
+ "timestamp": 1783688012207,
4
4
  "packageName": "@happyvertical/smrt-core",
5
- "packageVersion": "0.38.25",
5
+ "packageVersion": "0.38.26",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-core:SmrtClass": {
8
8
  "name": "smrtclass",
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-07-10T01:19:41.827Z",
3
+ "generatedAt": "2026-07-10T12:53:30.253Z",
4
4
  "packageName": "@happyvertical/smrt-core",
5
- "packageVersion": "0.38.25",
5
+ "packageVersion": "0.38.26",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "aa2cf3eb5f80eb6b293c0295d389642133c141565c82441c91218de621c739f6",
10
- "packageJson": "f264140cfb873ecdef0955f607995a879aed33484739988b61c0ebee16f85dd3",
9
+ "manifest": "ba1939aa15bc24f7b2c7afab6e67acbb659eb95950bfa192094f27e6ef910857",
10
+ "packageJson": "10ed64f544dff4a6dde408e676c2610865a5cdcd1507eb0b30fdaa9da2aeb422",
11
11
  "agents": "3d72a7c7113be59dd66046c50acc3e2759735fae5e9a0c6b30801d3e193b773d"
12
12
  },
13
13
  "exports": [
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAIlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAmB5D,YAAY,EACV,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAalC,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE;QACV,yDAAyD;QACzD,OAAO,EAAE,OAAO,CAAC;QACjB,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mDAAmD;QACnD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;WAIG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;WAGG;QACH,YAAY,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACrC;;;;WAIG;QACH,WAAW,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAC;YAAC,cAAc,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC9D,CAAC;IACF,mEAAmE;IACnE,SAAS,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA+BD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,mBAAmB,GAC5B,MAAM,CAaR;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAu5BlE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAIlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAoB5D,YAAY,EACV,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAalC,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE;QACV,yDAAyD;QACzD,OAAO,EAAE,OAAO,CAAC;QACjB,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mDAAmD;QACnD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;WAIG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB;;;WAGG;QACH,YAAY,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACrC;;;;WAIG;QACH,WAAW,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAC;YAAC,cAAc,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC9D,CAAC;IACF,mEAAmE;IACnE,SAAS,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AA+BD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,mBAAmB,GAC5B,MAAM,CAaR;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAu5BlE"}
@@ -1,5 +1,5 @@
1
- import { findCliApiCoherenceViolations, generateSvelteKitRoutes, methodNameToKebab, resolveApiActionSet, validateCliIncludeAgainstApi } from "./sveltekit-generator.js";
2
- import { buildWebCollectionDefinition, buildWebToolDescriptors, computeWebManifestHash, selectWebCollectionEntries } from "./web-collections.js";
1
+ import { findCliApiCoherenceViolations, generateSvelteKitRoutes, methodNameToKebab, resolveApiActionRouteConfig, resolveApiActionSet, validateCliIncludeAgainstApi } from "./sveltekit-generator.js";
2
+ import { buildWebCollectionDefinition, buildWebToolDescriptors, computeWebManifestHash, isCollectionManifestClass, selectWebCollectionEntries } from "./web-collections.js";
3
3
  import { buildDomainKnowledgeManifest } from "../knowledge.js";
4
4
  import { importWorkspaceModule } from "../utils/import-workspace-module.js";
5
5
  import { discoverSmrtPackages } from "../manifest/discover-smrt-packages.js";
@@ -589,14 +589,54 @@ export { setupRoutes as default };
589
589
  function lowerFirst(value) {
590
590
  return value ? value[0].toLowerCase() + value.slice(1) : value;
591
591
  }
592
- function uniqueApiClientEntries(objects) {
592
+ var GENERATED_CLIENT_CRUD_METHODS = /* @__PURE__ */ new Set([
593
+ "list",
594
+ "get",
595
+ "create",
596
+ "update",
597
+ "delete"
598
+ ]);
599
+ var GENERATED_CLIENT_BASE_METHODS = /* @__PURE__ */ new Set([...GENERATED_CLIENT_CRUD_METHODS, "search"]);
600
+ function findManifestObjectByName(manifest, name) {
601
+ return Object.entries(manifest.objects).find(([manifestKey, candidate]) => manifestKey === name || candidate.qualifiedName === name || candidate.className === name)?.[1];
602
+ }
603
+ function resolveCollectionItemObject(obj, manifest) {
604
+ const seen = /* @__PURE__ */ new Set();
605
+ let candidate = obj;
606
+ while (candidate) {
607
+ if (candidate.extendsTypeArg) {
608
+ const itemObject = findManifestObjectByName(manifest, candidate.extendsTypeArg);
609
+ if (itemObject) return itemObject;
610
+ }
611
+ if (candidate.className.endsWith("Collection")) {
612
+ const conventionalItem = findManifestObjectByName(manifest, candidate.className.slice(0, -10));
613
+ if (conventionalItem) return conventionalItem;
614
+ }
615
+ const parentName = candidate.extendsQualified || candidate.extends;
616
+ if (!parentName || seen.has(parentName)) return void 0;
617
+ seen.add(parentName);
618
+ candidate = findManifestObjectByName(manifest, parentName);
619
+ }
620
+ }
621
+ function resolveApiClientDataInterfaceName(obj, manifest) {
622
+ if (!isCollectionManifestClass(manifest, obj)) return `${obj.className}Data`;
623
+ return `${resolveCollectionItemObject(obj, manifest)?.className || obj.className}Data`;
624
+ }
625
+ function uniqueApiClientEntries(manifest) {
626
+ const canonicalCollectionOwners = /* @__PURE__ */ new Map();
627
+ const objects = Object.entries(manifest.objects);
628
+ for (const [, obj] of objects) {
629
+ const current = canonicalCollectionOwners.get(obj.collection);
630
+ if (!current || isCollectionManifestClass(manifest, current) && !isCollectionManifestClass(manifest, obj)) canonicalCollectionOwners.set(obj.collection, obj);
631
+ }
632
+ const reservedCanonicalKeys = new Set(canonicalCollectionOwners.keys());
593
633
  const usedKeys = /* @__PURE__ */ new Set();
594
634
  return objects.map(([objectName, obj]) => {
595
- let clientKey = obj.collection;
596
- if (usedKeys.has(clientKey)) clientKey = lowerFirst(obj.className);
635
+ const isCanonicalOwner = canonicalCollectionOwners.get(obj.collection) === obj;
636
+ let clientKey = isCanonicalOwner ? obj.collection : lowerFirst(obj.className);
597
637
  const baseClientKey = clientKey;
598
638
  let suffix = 2;
599
- while (usedKeys.has(clientKey)) {
639
+ while (usedKeys.has(clientKey) || !isCanonicalOwner && reservedCanonicalKeys.has(clientKey)) {
600
640
  clientKey = `${baseClientKey}${suffix}`;
601
641
  suffix += 1;
602
642
  }
@@ -615,28 +655,68 @@ function generateClientModule(manifest, options = {}) {
615
655
 
616
656
  ${CLIENT_FETCH_RUNTIME}
617
657
 
658
+ async function __smrtFetchActionResult(url, init) {
659
+ const body = await __smrtFetchJson(url, init);
660
+ return body &&
661
+ typeof body === 'object' &&
662
+ Object.prototype.hasOwnProperty.call(body, 'result')
663
+ ? body.result
664
+ : body;
665
+ }
666
+
667
+ function __smrtActionUrl(url, options, pathParamNames, includeQuery) {
668
+ const values = options && typeof options === 'object' ? options : {};
669
+ const pathParams = new Set(pathParamNames);
670
+ let resolvedUrl = url;
671
+ for (const name of pathParamNames) {
672
+ const value = values[name];
673
+ if (value === undefined || value === null) {
674
+ throw new Error('Missing generated client route parameter: ' + name);
675
+ }
676
+ resolvedUrl = resolvedUrl.replace(
677
+ '[' + name + ']',
678
+ encodeURIComponent(String(value)),
679
+ );
680
+ }
681
+
682
+ if (!includeQuery) return resolvedUrl;
683
+
684
+ const searchParams = new URLSearchParams();
685
+ for (const [name, value] of Object.entries(values)) {
686
+ if (pathParams.has(name) || value === undefined) continue;
687
+ if (Array.isArray(value)) {
688
+ for (const item of value) searchParams.append(name, String(item));
689
+ continue;
690
+ }
691
+ searchParams.append(
692
+ name,
693
+ value !== null && typeof value === 'object'
694
+ ? JSON.stringify(value)
695
+ : String(value),
696
+ );
697
+ }
698
+
699
+ const query = searchParams.toString();
700
+ return query ? resolvedUrl + '?' + query : resolvedUrl;
701
+ }
702
+
618
703
  export function createClient(basePath = '/api/v1') {
619
- return {${uniqueApiClientEntries(Object.entries(manifest.objects)).map(({ obj, clientKey }) => {
704
+ return {${uniqueApiClientEntries(manifest).map(({ obj, clientKey }) => {
620
705
  const { collection, methods = {} } = obj;
621
706
  const exposedActions = resolveApiActionSet(obj);
622
707
  const apiConfig = obj.decoratorConfig?.api;
623
- const apiRoutes = apiConfig && typeof apiConfig === "object" ? apiConfig.routes ?? {} : {};
624
- const customMethods = Object.entries(methods).filter(([name, method]) => method.isPublic && exposedActions.has(name));
625
- const segmentFor = (methodName) => {
626
- const overridePath = apiRoutes[methodName]?.path;
627
- if (overridePath) {
628
- const trimmed = overridePath.split("/").map((s) => s.trim()).filter(Boolean).join("/");
629
- if (trimmed.length > 0) return trimmed;
630
- }
631
- return options.kebabRoutes ? methodNameToKebab(methodName) : methodName;
632
- };
633
- const customMethodImpls = customMethods.map(([methodName, _method]) => {
634
- const urlSegment = segmentFor(methodName);
635
- return ` ${methodName}: (id, options) => __smrtFetchJson(basePath + '/${collection}/' + id + '/${urlSegment}', {
636
- method: 'POST',
708
+ const customMethods = Object.entries(methods).filter(([name, method]) => !GENERATED_CLIENT_CRUD_METHODS.has(name) && method.isPublic && exposedActions.has(name));
709
+ const customMethodImpls = customMethods.map(([methodName, method]) => {
710
+ const routeConfig = resolveApiActionRouteConfig(methodName, method, apiConfig, { kebabRoutes: options.kebabRoutes }, isCollectionManifestClass(manifest, obj) ? "collection" : method.isStatic ? "collection" : "item");
711
+ const urlSegment = routeConfig.pathSegments.join("/");
712
+ return ` ${methodName}: (${routeConfig.scope === "collection" ? "options" : "id, options"}) => __smrtFetchActionResult(${`__smrtActionUrl(${routeConfig.scope === "collection" ? `basePath + '/${collection}/${urlSegment}'` : `basePath + '/${collection}/' + id + '/${urlSegment}'`}, options, ${JSON.stringify(routeConfig.pathParamNames)}, ${routeConfig.method === "GET"})`}, ${routeConfig.method === "GET" ? `{
713
+ method: 'GET',
714
+ headers: { 'Accept': 'application/json' }
715
+ }` : `{
716
+ method: '${routeConfig.method}',
637
717
  headers: { 'Content-Type': 'application/json' },
638
718
  body: JSON.stringify(options || {})
639
- })`;
719
+ }`})`;
640
720
  }).join(",\n");
641
721
  return `
642
722
  ${clientKey}: {
@@ -900,17 +980,35 @@ ${Object.entries(obj.fields).map(([fieldName, field]) => {
900
980
  updated_at?: string;
901
981
  }`;
902
982
  }).join("\n\n");
903
- const apiClientInterface = uniqueApiClientEntries(Object.entries(manifest.objects)).map(({ obj, clientKey }) => {
904
- const { className, methods = {} } = obj;
905
- const interfaceName = `${className}Data`;
983
+ const apiClientInterface = uniqueApiClientEntries(manifest).map(({ obj, clientKey }) => {
984
+ const { methods = {} } = obj;
985
+ const apiConfig = obj.decoratorConfig?.api;
986
+ const interfaceName = resolveApiClientDataInterfaceName(obj, manifest);
906
987
  const exposedActions = resolveApiActionSet(obj);
907
- const customMethods = Object.entries(methods).filter(([name, method]) => method.isPublic && exposedActions.has(name));
988
+ const customMethods = Object.entries(methods).filter(([name, method]) => !GENERATED_CLIENT_CRUD_METHODS.has(name) && method.isPublic && exposedActions.has(name));
908
989
  const customMethodSignatures = customMethods.map(([methodName, method]) => {
990
+ const routeConfig = resolveApiActionRouteConfig(methodName, method, apiConfig, {}, isCollectionManifestClass(manifest, obj) ? "collection" : method.isStatic ? "collection" : "item");
909
991
  const params = method.parameters || [];
910
- return ` ${methodName}(id: string${params.length > 0 ? `, options?: { ${params.map((p) => `${p.name}?: ${mapTypeScriptType(p.type)}`).join("; ")} }` : ""}): Promise<any>;`;
992
+ const pathParamNames = new Set(routeConfig.pathParamNames);
993
+ const hasSingleOptionsParameter = params.length === 1 && params[0]?.name === "options";
994
+ const requiredPathProperties = routeConfig.pathParamNames.map((name) => `${name}: string`).join("; ");
995
+ let optionsType;
996
+ if (hasSingleOptionsParameter) {
997
+ const directOptionsType = mapTypeScriptType(params[0].type);
998
+ optionsType = requiredPathProperties ? `${directOptionsType} & { ${requiredPathProperties} }` : directOptionsType;
999
+ } else {
1000
+ const parameterNames = new Set(params.map((param) => param.name));
1001
+ const properties = params.map((param) => `${param.name}${pathParamNames.has(param.name) ? "" : "?"}: ${mapTypeScriptType(param.type)}`);
1002
+ for (const pathParamName of routeConfig.pathParamNames) if (!parameterNames.has(pathParamName)) properties.push(`${pathParamName}: string`);
1003
+ optionsType = properties.length > 0 ? `{ ${properties.join("; ")} }` : "Record<string, never>";
1004
+ }
1005
+ const optionsParameter = `${routeConfig.pathParamNames.length > 0 ? "options" : "options?"}: ${optionsType}`;
1006
+ return ` ${methodName}(${routeConfig.scope === "collection" ? optionsParameter : `id: string, ${optionsParameter}`}): Promise<any>;`;
911
1007
  }).join("\n");
912
- if (customMethods.length > 0) return ` ${clientKey}: CrudOperations<${interfaceName}> & {\n${customMethodSignatures}\n };`;
913
- else return ` ${clientKey}: CrudOperations<${interfaceName}>;`;
1008
+ const overriddenBaseMethods = customMethods.map(([methodName]) => methodName).filter((methodName) => GENERATED_CLIENT_BASE_METHODS.has(methodName));
1009
+ const crudOperationsType = overriddenBaseMethods.length > 0 ? `Omit<CrudOperations<${interfaceName}>, ${overriddenBaseMethods.map((methodName) => JSON.stringify(methodName)).join(" | ")}>` : `CrudOperations<${interfaceName}>`;
1010
+ if (customMethods.length > 0) return ` ${clientKey}: ${crudOperationsType} & {\n${customMethodSignatures}\n };`;
1011
+ else return ` ${clientKey}: ${crudOperationsType};`;
914
1012
  }).join("\n");
915
1013
  const webCollectionInterface = selectWebCollectionEntries(manifest).map(({ collection, obj }) => ` ${collection}: SmrtWebCollectionDefinition<import('@happyvertical/smrt-virt-types').${obj.className}Data>;`).join("\n");
916
1014
  Object.entries(manifest.objects).flatMap(([_name, obj]) => Object.entries(obj.methods).map(([methodName, method]) => ({
@@ -989,7 +1087,8 @@ declare module '@happyvertical/smrt-virt-routes' {
989
1087
  // list/search, a bare object for get/create/update — with snake_case field
990
1088
  // names (created_at, updated_at). These declarations match that shape (no
991
1089
  // envelope, no camelCase). Fetchers reject on non-2xx with a SmrtClientError
992
- // (#1796). This is byte-identical to the prebuild declaration path.
1090
+ // (#1796). Shared CRUD wire types stay aligned with the prebuild declaration
1091
+ // path; Vite-only custom action declarations are layered onto them below.
993
1092
  declare module '@happyvertical/smrt-virt-client' {
994
1093
  /** Shape of a JSON error body carried by a rejected request (SmrtClientError.body). */
995
1094
  export interface ApiError {