@zapier/zapier-sdk 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @zapier/zapier-sdk
2
2
 
3
+ ## 0.13.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 325d5c2: Fix bug where adding apps with dashed slugs would create types with syntax errors. All valid keys are now valid properties on the apps interface.
8
+
9
+ ## 0.13.1
10
+
11
+ ### Patch Changes
12
+
13
+ - a607e3a: Fixes a bug where listApps would return all apps if no appKeys were resolved to app locators. This in turn fixes the `add` command hanging when using only invalid app keys (since it would try to add all apps), and it fixes getApp which returned the first app of all apps for an invalid slug, since it just calls to listApps.
14
+
3
15
  ## 0.13.0
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -30,9 +30,14 @@
30
30
  ## Installation
31
31
 
32
32
  ```bash
33
+ # If starting a new project:
34
+ cd your-project-dir
35
+ npm init -y
36
+ npx tsc --init
37
+ # Also, set `"type": "module"` in your package.json for the code examples below.
38
+
33
39
  npm install @zapier/zapier-sdk
34
40
  npm install -D @zapier/zapier-sdk-cli @types/node typescript
35
- npx tsc --init
36
41
  ```
37
42
 
38
43
  ## Quick Start
@@ -41,15 +46,22 @@ npx tsc --init
41
46
  # Authenticates through your browser, automatically handling token management
42
47
  npx zapier-sdk login
43
48
 
44
- # Generate TypeScript types for the request/response signatures of apps.
45
- # By default, the types will be generated inside of your `src` folder, if you have one, or the root folder.
46
- # To use these types, ensure they are added to your `tsconfig.json` file.
49
+ # Generate TypeScript types for actions and fields of any apps you want to use.
50
+ npx zapier-sdk add slack google-sheets
47
51
 
48
- # Single app
49
- npx zapier-sdk add slack --types-output ./types/slack.ts
52
+ # The names of apps are there slugs if available, otherwise internal keys. If
53
+ # you don't know the slug or key, just use `list-apps` search to find it.
54
+ zapier-sdk list-apps --search "google sheets"
50
55
 
51
- # Multiple apps
52
- npx zapier-sdk add slack github trello
56
+ # The output will show you the valid keys next to the app title like this:
57
+ # 1. Google Sheets (GoogleSheetsV2CLIAPI, google-sheets, google_sheets)
58
+
59
+ # By default, types will be generated inside your `src` or `lib` folder if you
60
+ # have one or otherwise the root folder. Make sure your `tsconfig.json` file
61
+ # includes the generated type files.
62
+
63
+ # Alternatively, you can specify a custom output directory.
64
+ npx zapier-sdk add slack google-sheets --types-output ./types
53
65
  ```
54
66
 
55
67
  ```typescript
@@ -94,7 +106,7 @@ for await (const app of zapier.listApps({ maxItems: 100 }).items()) {
94
106
 
95
107
  // You can collect all results, but this could take a while for long lists!
96
108
  const allApps = await Array.fromAsync(
97
- zapier.listApps({ maxItems: 1000 }).items(),
109
+ zapier.listApps({ maxItems: 100 }).items(),
98
110
  );
99
111
 
100
112
  console.log({
@@ -115,6 +127,10 @@ const { data: myAuths } = await zapier.listAuthentications({
115
127
  owner: "me",
116
128
  });
117
129
 
130
+ console.log({
131
+ myAuths,
132
+ });
133
+
118
134
  // ######## Find Actions ########
119
135
  // Option 1: List actions
120
136
  // Remember, this is just the first page which may not include them all!
@@ -157,7 +173,13 @@ console.log({
157
173
  const { data: profile } = await zapier.getProfile();
158
174
 
159
175
  // Option 2:
160
- const { data: users } = await zapier.apps.slack.search.user_by_email({
176
+ // Create an app binding to your authentication.
177
+ const mySlack = zapier.apps.slack({
178
+ authenticationId: myAuths[0].id,
179
+ });
180
+
181
+ // Use your app binding to run the action.
182
+ const { data: users } = await mySlack.search.user_by_email({
161
183
  inputs: {
162
184
  email: profile.email,
163
185
  },
@@ -166,6 +188,23 @@ const { data: users } = await zapier.apps.slack.search.user_by_email({
166
188
  console.log({
167
189
  users,
168
190
  });
191
+
192
+ // You can also skip the app binding and pass the auth into the action.
193
+ const { data: sameUsers } = await zapier.apps.slack.search.user_by_email({
194
+ inputs: {
195
+ email: profile.email,
196
+ },
197
+ authenticationId: myAuths[0].id,
198
+ });
199
+
200
+ console.log({
201
+ sameUsers,
202
+ });
203
+
204
+ // If the slug for an app has dashes, you can also use a snake_cased app key.
205
+ // For example, either of the following are valid:
206
+ // zapier.apps["google-sheets"]
207
+ // zapier.apps.google_sheets
169
208
  ```
170
209
 
171
210
  ## Available Functions
package/dist/index.cjs CHANGED
@@ -1082,6 +1082,18 @@ zod.z.object({
1082
1082
  links: NeedChoicesResponseLinksSchema.optional()
1083
1083
  });
1084
1084
 
1085
+ // src/utils/string-utils.ts
1086
+ function toTitleCase(input) {
1087
+ return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1088
+ }
1089
+ function toSnakeCase(input) {
1090
+ let result = input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s\-]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
1091
+ if (/^[0-9]/.test(result)) {
1092
+ result = "_" + result;
1093
+ }
1094
+ return result;
1095
+ }
1096
+
1085
1097
  // src/schemas/App.ts
1086
1098
  var AppItemSchema = withFormatter(
1087
1099
  ImplementationMetaSchema.omit({ name: true, id: true }).extend({
@@ -1094,10 +1106,18 @@ var AppItemSchema = withFormatter(
1094
1106
  }),
1095
1107
  {
1096
1108
  format: (item) => {
1109
+ const additionalKeys = [];
1110
+ if (item.slug && item.slug !== item.key) {
1111
+ additionalKeys.push(item.slug);
1112
+ const snakeCaseSlug = toSnakeCase(item.slug);
1113
+ if (snakeCaseSlug !== item.slug && snakeCaseSlug !== item.key) {
1114
+ additionalKeys.push(snakeCaseSlug);
1115
+ }
1116
+ }
1097
1117
  return {
1098
1118
  title: item.title,
1099
1119
  key: item.key,
1100
- keys: [item.slug, item.key].filter(Boolean),
1120
+ keys: [item.key, ...additionalKeys],
1101
1121
  description: item.description,
1102
1122
  details: []
1103
1123
  };
@@ -1119,15 +1139,11 @@ function normalizeImplementationMetaToAppItem(implementationMeta) {
1119
1139
  const [selectedApi, appVersion] = splitVersionedKey(implementationMeta.id);
1120
1140
  const { id, name, ...restOfImplementationMeta } = implementationMeta;
1121
1141
  return {
1122
- // Pass through all ImplementationMeta fields except id and name
1123
1142
  ...restOfImplementationMeta,
1124
- // Transform key fields
1125
1143
  title: name,
1126
1144
  key: selectedApi,
1127
1145
  implementation_id: id,
1128
- // Keep the full versioned ID
1129
1146
  version: appVersion
1130
- // Extract version separately
1131
1147
  };
1132
1148
  }
1133
1149
  function normalizeAuthenticationItem(auth, options = {}) {
@@ -1235,10 +1251,10 @@ function toImplementationId(appLocator) {
1235
1251
  // src/plugins/listApps/index.ts
1236
1252
  var listAppsPlugin = ({ context }) => {
1237
1253
  const listApps = createPaginatedFunction(async function listAppsPage(options) {
1238
- const api = context.api;
1239
- const opts = options;
1240
- const appLocators = await context.resolveAppKeys({
1241
- appKeys: [...opts.appKeys ?? []]
1254
+ const { api, resolveAppKeys: resolveAppKeys2 } = context;
1255
+ const appKeys = options.appKeys ?? [];
1256
+ const appLocators = await resolveAppKeys2({
1257
+ appKeys: [...appKeys]
1242
1258
  });
1243
1259
  const implementationNameToLocator = {};
1244
1260
  for (const locator of appLocators) {
@@ -1253,9 +1269,9 @@ var listAppsPlugin = ({ context }) => {
1253
1269
  `Duplicate lookup app keys found: ${duplicatedLookupAppKeys.join(", ")}`
1254
1270
  );
1255
1271
  }
1256
- if (opts.search) {
1272
+ if (options.search) {
1257
1273
  const searchParams2 = {};
1258
- searchParams2.term = opts.search;
1274
+ searchParams2.term = options.search;
1259
1275
  const searchEnvelope = await api.get(
1260
1276
  "/api/v4/implementations-meta/search/",
1261
1277
  {
@@ -1280,16 +1296,22 @@ var listAppsPlugin = ({ context }) => {
1280
1296
  }
1281
1297
  }
1282
1298
  const searchParams = {};
1283
- if (opts.pageSize) {
1284
- searchParams.limit = opts.pageSize.toString();
1299
+ if (options.pageSize) {
1300
+ searchParams.limit = options.pageSize.toString();
1285
1301
  }
1286
1302
  if (appLocators.length === 0) {
1287
1303
  searchParams.latest_only = "true";
1288
1304
  }
1289
- if (opts.cursor) {
1290
- searchParams.offset = opts.cursor;
1305
+ if (options.cursor) {
1306
+ searchParams.offset = options.cursor;
1291
1307
  }
1292
1308
  searchParams.selected_apis = appLocators.map((locator) => toImplementationId(locator)).join(",");
1309
+ if (appKeys.length > 0 && appLocators.length === 0) {
1310
+ return {
1311
+ data: [],
1312
+ nextCursor: void 0
1313
+ };
1314
+ }
1293
1315
  const implementationsEnvelope = await api.get(
1294
1316
  "/api/v4/implementations-meta/lookup/",
1295
1317
  {
@@ -1767,11 +1789,6 @@ var RootFieldItemSchema = zod.z.union([
1767
1789
  FieldsetItemSchema
1768
1790
  ]);
1769
1791
 
1770
- // src/utils/string-utils.ts
1771
- function toTitleCase(input) {
1772
- return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1773
- }
1774
-
1775
1792
  // src/plugins/listInputFields/index.ts
1776
1793
  function getInputFieldTypeFromNeed(need) {
1777
1794
  if (need.list) {
@@ -3876,3 +3893,5 @@ exports.readManifestFromFile = readManifestFromFile;
3876
3893
  exports.registryPlugin = registryPlugin;
3877
3894
  exports.requestPlugin = requestPlugin;
3878
3895
  exports.runActionPlugin = runActionPlugin;
3896
+ exports.toSnakeCase = toSnakeCase;
3897
+ exports.toTitleCase = toTitleCase;
package/dist/index.d.mts CHANGED
@@ -2885,6 +2885,27 @@ type ParamsProperty = z.infer<typeof ParamsPropertySchema>;
2885
2885
  */
2886
2886
  declare function createFunction<TOptions, TResult, TSchemaOptions extends TOptions = TOptions>(coreFn: (options: TOptions) => Promise<TResult>, schema?: z.ZodSchema<TSchemaOptions>): (options?: TOptions) => Promise<TResult>;
2887
2887
 
2888
+ /**
2889
+ * String utility functions for the Zapier SDK
2890
+ */
2891
+ /**
2892
+ * Converts a string to title case, handling various input formats:
2893
+ * - camelCase: "firstName" → "First Name"
2894
+ * - snake_case: "first_name" → "First Name"
2895
+ * - kebab-case: "first-name" → "First Name"
2896
+ * - mixed formats: "first_name-value" → "First Name Value"
2897
+ */
2898
+ declare function toTitleCase(input: string): string;
2899
+ /**
2900
+ * Converts a string to snake_case, handling various input formats:
2901
+ * - camelCase: "firstName" → "first_name"
2902
+ * - kebab-case: "first-name" → "first_name"
2903
+ * - title case: "First Name" → "first_name"
2904
+ * - mixed formats: "first-Name Value" → "first_name_value"
2905
+ * - starts with number: "123abc" → "_123abc"
2906
+ */
2907
+ declare function toSnakeCase(input: string): string;
2908
+
2888
2909
  declare const appKeyResolver: StaticResolver;
2889
2910
 
2890
2911
  interface ActionTypeItem {
@@ -3081,4 +3102,4 @@ declare function createZapierSdkWithoutRegistry(options?: ZapierSdkOptions): Sdk
3081
3102
  }>;
3082
3103
  declare function createZapierSdk(options?: ZapierSdkOptions): ZapierSdk;
3083
3104
 
3084
- export { type Action, type ActionExecutionOptions, type ActionExecutionResult, type ActionField, type ActionFieldChoice, type ActionItem$1 as ActionItem, type ActionKeyProperty, ActionKeyPropertySchema, type ActionTypeProperty, ActionTypePropertySchema, type ApiError, type ApiEvent, type ApiPluginOptions, type ApiPluginProvides, type App, type AppItem, type AppKeyProperty, AppKeyPropertySchema, type AppsPluginProvides, type AuthEvent, type AuthOptions, type Authentication, type AuthenticationIdProperty, AuthenticationIdPropertySchema, type AuthenticationItem, type AuthenticationsResponse, type Choice, DEFAULT_CONFIG_PATH, type DebugProperty, DebugPropertySchema, type ErrorOptions, type EventCallback, type FetchPluginProvides, type Field, type FieldsetItem, type FindFirstAuthenticationPluginProvides, type FindUniqueAuthenticationPluginProvides, type FormatMetadata, type FormattedItem, type FunctionOptions, type FunctionRegistryEntry, type GetActionPluginProvides, type GetAppPluginProvides, type GetAuthenticationPluginProvides, type GetContextType, type GetProfilePluginProvides, type GetSdkType, type InfoFieldItem, type InputFieldItem, type InputsProperty, InputsPropertySchema, type LimitProperty, LimitPropertySchema, type ListActionsPluginProvides, type ListAppsPluginProvides, type ListAuthenticationsPluginProvides, type ListInputFieldsPluginProvides, type LoadingEvent, type ManifestPluginOptions, type ManifestPluginProvides, type Need, type NeedsRequest, type NeedsResponse, type OffsetProperty, OffsetPropertySchema, type OutputProperty, OutputPropertySchema, type PaginatedSdkFunction, type ParamsProperty, ParamsPropertySchema, type Plugin, type PluginDependencies, type PluginOptions, type PluginProvides, type PositionalMetadata, RelayFetchSchema, RelayRequestSchema, type RequestPluginProvides, type RootFieldItem, type RunActionPluginProvides, type Sdk, type SdkEvent, type UserProfile, type UserProfileItem, ZapierActionError, ZapierApiError, ZapierAppNotFoundError, ZapierAuthenticationError, ZapierBundleError, ZapierConfigurationError, ZapierError, type ZapierFetchInitOptions, ZapierNotFoundError, ZapierResourceNotFoundError, type ZapierSdk, type ZapierSdkApps, type ZapierSdkOptions, ZapierTimeoutError, ZapierUnknownError, ZapierValidationError, actionKeyResolver, actionTypeResolver, apiPlugin, appKeyResolver, appsPlugin, authenticationIdGenericResolver, authenticationIdResolver, createFunction, createSdk, createZapierSdk, createZapierSdkWithoutRegistry, fetchPlugin, findFirstAuthenticationPlugin, findManifestEntry, findUniqueAuthenticationPlugin, formatErrorMessage, getActionPlugin, getAppPlugin, getAuthenticationPlugin, getPreferredManifestEntryKey, getProfilePlugin, getTokenFromCliLogin, getTokenFromEnv, getTokenFromEnvOrConfig, inputFieldKeyResolver, inputsAllOptionalResolver, inputsResolver, isPositional, listActionsPlugin, listAppsPlugin, listAuthenticationsPlugin, listInputFieldsPlugin, manifestPlugin, readManifestFromFile, registryPlugin, requestPlugin, runActionPlugin };
3105
+ export { type Action, type ActionExecutionOptions, type ActionExecutionResult, type ActionField, type ActionFieldChoice, type ActionItem$1 as ActionItem, type ActionKeyProperty, ActionKeyPropertySchema, type ActionTypeProperty, ActionTypePropertySchema, type ApiError, type ApiEvent, type ApiPluginOptions, type ApiPluginProvides, type App, type AppItem, type AppKeyProperty, AppKeyPropertySchema, type AppsPluginProvides, type AuthEvent, type AuthOptions, type Authentication, type AuthenticationIdProperty, AuthenticationIdPropertySchema, type AuthenticationItem, type AuthenticationsResponse, type Choice, DEFAULT_CONFIG_PATH, type DebugProperty, DebugPropertySchema, type ErrorOptions, type EventCallback, type FetchPluginProvides, type Field, type FieldsetItem, type FindFirstAuthenticationPluginProvides, type FindUniqueAuthenticationPluginProvides, type FormatMetadata, type FormattedItem, type FunctionOptions, type FunctionRegistryEntry, type GetActionPluginProvides, type GetAppPluginProvides, type GetAuthenticationPluginProvides, type GetContextType, type GetProfilePluginProvides, type GetSdkType, type InfoFieldItem, type InputFieldItem, type InputsProperty, InputsPropertySchema, type LimitProperty, LimitPropertySchema, type ListActionsPluginProvides, type ListAppsPluginProvides, type ListAuthenticationsPluginProvides, type ListInputFieldsPluginProvides, type LoadingEvent, type ManifestPluginOptions, type ManifestPluginProvides, type Need, type NeedsRequest, type NeedsResponse, type OffsetProperty, OffsetPropertySchema, type OutputProperty, OutputPropertySchema, type PaginatedSdkFunction, type ParamsProperty, ParamsPropertySchema, type Plugin, type PluginDependencies, type PluginOptions, type PluginProvides, type PositionalMetadata, RelayFetchSchema, RelayRequestSchema, type RequestPluginProvides, type RootFieldItem, type RunActionPluginProvides, type Sdk, type SdkEvent, type UserProfile, type UserProfileItem, ZapierActionError, ZapierApiError, ZapierAppNotFoundError, ZapierAuthenticationError, ZapierBundleError, ZapierConfigurationError, ZapierError, type ZapierFetchInitOptions, ZapierNotFoundError, ZapierResourceNotFoundError, type ZapierSdk, type ZapierSdkApps, type ZapierSdkOptions, ZapierTimeoutError, ZapierUnknownError, ZapierValidationError, actionKeyResolver, actionTypeResolver, apiPlugin, appKeyResolver, appsPlugin, authenticationIdGenericResolver, authenticationIdResolver, createFunction, createSdk, createZapierSdk, createZapierSdkWithoutRegistry, fetchPlugin, findFirstAuthenticationPlugin, findManifestEntry, findUniqueAuthenticationPlugin, formatErrorMessage, getActionPlugin, getAppPlugin, getAuthenticationPlugin, getPreferredManifestEntryKey, getProfilePlugin, getTokenFromCliLogin, getTokenFromEnv, getTokenFromEnvOrConfig, inputFieldKeyResolver, inputsAllOptionalResolver, inputsResolver, isPositional, listActionsPlugin, listAppsPlugin, listAuthenticationsPlugin, listInputFieldsPlugin, manifestPlugin, readManifestFromFile, registryPlugin, requestPlugin, runActionPlugin, toSnakeCase, toTitleCase };
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ export type { Action, App, Need, Field, Choice, ActionExecutionResult, ActionFie
22
22
  export { isPositional, PositionalMetadata } from "./utils/schema-utils";
23
23
  export { createFunction } from "./utils/function-utils";
24
24
  export type { FormattedItem, FormatMetadata } from "./utils/schema-utils";
25
+ export { toSnakeCase, toTitleCase } from "./utils/string-utils";
25
26
  export * from "./resolvers";
26
27
  export * from "./auth";
27
28
  export { RelayRequestSchema, RelayFetchSchema, } from "./plugins/request/schemas";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAG9B,YAAY,EACV,MAAM,EACN,GAAG,EACH,IAAI,EACJ,KAAK,EACL,MAAM,EACN,qBAAqB,EACrB,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,WAAW,GACZ,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG1E,cAAc,aAAa,CAAC;AAG5B,cAAc,QAAQ,CAAC;AAIvB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,eAAe,EACf,8BAA8B,EAC9B,SAAS,EACT,gBAAgB,GACjB,MAAM,OAAO,CAAC;AAGf,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,YAAY,EACV,MAAM,EACN,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,cAAc,EACd,GAAG,GACJ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAG9B,YAAY,EACV,MAAM,EACN,GAAG,EACH,IAAI,EACJ,KAAK,EACL,MAAM,EACN,qBAAqB,EACrB,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,WAAW,GACZ,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG1E,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGhE,cAAc,aAAa,CAAC;AAG5B,cAAc,QAAQ,CAAC;AAIvB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,eAAe,EACf,8BAA8B,EAC9B,SAAS,EACT,gBAAgB,GACjB,MAAM,OAAO,CAAC;AAGf,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,YAAY,EACV,MAAM,EACN,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,cAAc,EACd,GAAG,GACJ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -22,6 +22,8 @@ export * from "./plugins/api";
22
22
  // Export schema utilities for CLI
23
23
  export { isPositional } from "./utils/schema-utils";
24
24
  export { createFunction } from "./utils/function-utils";
25
+ // Export string utilities
26
+ export { toSnakeCase, toTitleCase } from "./utils/string-utils";
25
27
  // Export resolver utilities for CLI
26
28
  export * from "./resolvers";
27
29
  // Export auth utilities for CLI use
package/dist/index.mjs CHANGED
@@ -1080,6 +1080,18 @@ z.object({
1080
1080
  links: NeedChoicesResponseLinksSchema.optional()
1081
1081
  });
1082
1082
 
1083
+ // src/utils/string-utils.ts
1084
+ function toTitleCase(input) {
1085
+ return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1086
+ }
1087
+ function toSnakeCase(input) {
1088
+ let result = input.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s\-]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
1089
+ if (/^[0-9]/.test(result)) {
1090
+ result = "_" + result;
1091
+ }
1092
+ return result;
1093
+ }
1094
+
1083
1095
  // src/schemas/App.ts
1084
1096
  var AppItemSchema = withFormatter(
1085
1097
  ImplementationMetaSchema.omit({ name: true, id: true }).extend({
@@ -1092,10 +1104,18 @@ var AppItemSchema = withFormatter(
1092
1104
  }),
1093
1105
  {
1094
1106
  format: (item) => {
1107
+ const additionalKeys = [];
1108
+ if (item.slug && item.slug !== item.key) {
1109
+ additionalKeys.push(item.slug);
1110
+ const snakeCaseSlug = toSnakeCase(item.slug);
1111
+ if (snakeCaseSlug !== item.slug && snakeCaseSlug !== item.key) {
1112
+ additionalKeys.push(snakeCaseSlug);
1113
+ }
1114
+ }
1095
1115
  return {
1096
1116
  title: item.title,
1097
1117
  key: item.key,
1098
- keys: [item.slug, item.key].filter(Boolean),
1118
+ keys: [item.key, ...additionalKeys],
1099
1119
  description: item.description,
1100
1120
  details: []
1101
1121
  };
@@ -1117,15 +1137,11 @@ function normalizeImplementationMetaToAppItem(implementationMeta) {
1117
1137
  const [selectedApi, appVersion] = splitVersionedKey(implementationMeta.id);
1118
1138
  const { id, name, ...restOfImplementationMeta } = implementationMeta;
1119
1139
  return {
1120
- // Pass through all ImplementationMeta fields except id and name
1121
1140
  ...restOfImplementationMeta,
1122
- // Transform key fields
1123
1141
  title: name,
1124
1142
  key: selectedApi,
1125
1143
  implementation_id: id,
1126
- // Keep the full versioned ID
1127
1144
  version: appVersion
1128
- // Extract version separately
1129
1145
  };
1130
1146
  }
1131
1147
  function normalizeAuthenticationItem(auth, options = {}) {
@@ -1233,10 +1249,10 @@ function toImplementationId(appLocator) {
1233
1249
  // src/plugins/listApps/index.ts
1234
1250
  var listAppsPlugin = ({ context }) => {
1235
1251
  const listApps = createPaginatedFunction(async function listAppsPage(options) {
1236
- const api = context.api;
1237
- const opts = options;
1238
- const appLocators = await context.resolveAppKeys({
1239
- appKeys: [...opts.appKeys ?? []]
1252
+ const { api, resolveAppKeys: resolveAppKeys2 } = context;
1253
+ const appKeys = options.appKeys ?? [];
1254
+ const appLocators = await resolveAppKeys2({
1255
+ appKeys: [...appKeys]
1240
1256
  });
1241
1257
  const implementationNameToLocator = {};
1242
1258
  for (const locator of appLocators) {
@@ -1251,9 +1267,9 @@ var listAppsPlugin = ({ context }) => {
1251
1267
  `Duplicate lookup app keys found: ${duplicatedLookupAppKeys.join(", ")}`
1252
1268
  );
1253
1269
  }
1254
- if (opts.search) {
1270
+ if (options.search) {
1255
1271
  const searchParams2 = {};
1256
- searchParams2.term = opts.search;
1272
+ searchParams2.term = options.search;
1257
1273
  const searchEnvelope = await api.get(
1258
1274
  "/api/v4/implementations-meta/search/",
1259
1275
  {
@@ -1278,16 +1294,22 @@ var listAppsPlugin = ({ context }) => {
1278
1294
  }
1279
1295
  }
1280
1296
  const searchParams = {};
1281
- if (opts.pageSize) {
1282
- searchParams.limit = opts.pageSize.toString();
1297
+ if (options.pageSize) {
1298
+ searchParams.limit = options.pageSize.toString();
1283
1299
  }
1284
1300
  if (appLocators.length === 0) {
1285
1301
  searchParams.latest_only = "true";
1286
1302
  }
1287
- if (opts.cursor) {
1288
- searchParams.offset = opts.cursor;
1303
+ if (options.cursor) {
1304
+ searchParams.offset = options.cursor;
1289
1305
  }
1290
1306
  searchParams.selected_apis = appLocators.map((locator) => toImplementationId(locator)).join(",");
1307
+ if (appKeys.length > 0 && appLocators.length === 0) {
1308
+ return {
1309
+ data: [],
1310
+ nextCursor: void 0
1311
+ };
1312
+ }
1291
1313
  const implementationsEnvelope = await api.get(
1292
1314
  "/api/v4/implementations-meta/lookup/",
1293
1315
  {
@@ -1765,11 +1787,6 @@ var RootFieldItemSchema = z.union([
1765
1787
  FieldsetItemSchema
1766
1788
  ]);
1767
1789
 
1768
- // src/utils/string-utils.ts
1769
- function toTitleCase(input) {
1770
- return input.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_\-]+/g, " ").replace(/\s+/g, " ").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1771
- }
1772
-
1773
1790
  // src/plugins/listInputFields/index.ts
1774
1791
  function getInputFieldTypeFromNeed(need) {
1775
1792
  if (need.list) {
@@ -3812,4 +3829,4 @@ function createZapierSdk(options = {}) {
3812
3829
  return createZapierSdkWithoutRegistry(options).addPlugin(registryPlugin);
3813
3830
  }
3814
3831
 
3815
- export { ActionKeyPropertySchema, ActionTypePropertySchema, AppKeyPropertySchema, AuthenticationIdPropertySchema, DEFAULT_CONFIG_PATH, DebugPropertySchema, InputsPropertySchema, LimitPropertySchema, OffsetPropertySchema, OutputPropertySchema, ParamsPropertySchema, RelayFetchSchema, RelayRequestSchema, ZapierActionError, ZapierApiError, ZapierAppNotFoundError, ZapierAuthenticationError, ZapierBundleError, ZapierConfigurationError, ZapierError, ZapierNotFoundError, ZapierResourceNotFoundError, ZapierTimeoutError, ZapierUnknownError, ZapierValidationError, actionKeyResolver, actionTypeResolver, apiPlugin, appKeyResolver, appsPlugin, authenticationIdGenericResolver, authenticationIdResolver, createFunction, createSdk, createZapierSdk, createZapierSdkWithoutRegistry, fetchPlugin, findFirstAuthenticationPlugin, findManifestEntry, findUniqueAuthenticationPlugin, formatErrorMessage, getActionPlugin, getAppPlugin, getAuthenticationPlugin, getPreferredManifestEntryKey, getProfilePlugin, getTokenFromCliLogin, getTokenFromEnv, getTokenFromEnvOrConfig, inputFieldKeyResolver, inputsAllOptionalResolver, inputsResolver, isPositional, listActionsPlugin, listAppsPlugin, listAuthenticationsPlugin, listInputFieldsPlugin, manifestPlugin, readManifestFromFile, registryPlugin, requestPlugin, runActionPlugin };
3832
+ export { ActionKeyPropertySchema, ActionTypePropertySchema, AppKeyPropertySchema, AuthenticationIdPropertySchema, DEFAULT_CONFIG_PATH, DebugPropertySchema, InputsPropertySchema, LimitPropertySchema, OffsetPropertySchema, OutputPropertySchema, ParamsPropertySchema, RelayFetchSchema, RelayRequestSchema, ZapierActionError, ZapierApiError, ZapierAppNotFoundError, ZapierAuthenticationError, ZapierBundleError, ZapierConfigurationError, ZapierError, ZapierNotFoundError, ZapierResourceNotFoundError, ZapierTimeoutError, ZapierUnknownError, ZapierValidationError, actionKeyResolver, actionTypeResolver, apiPlugin, appKeyResolver, appsPlugin, authenticationIdGenericResolver, authenticationIdResolver, createFunction, createSdk, createZapierSdk, createZapierSdkWithoutRegistry, fetchPlugin, findFirstAuthenticationPlugin, findManifestEntry, findUniqueAuthenticationPlugin, formatErrorMessage, getActionPlugin, getAppPlugin, getAuthenticationPlugin, getPreferredManifestEntryKey, getProfilePlugin, getTokenFromCliLogin, getTokenFromEnv, getTokenFromEnvOrConfig, inputFieldKeyResolver, inputsAllOptionalResolver, inputsResolver, isPositional, listActionsPlugin, listAppsPlugin, listAuthenticationsPlugin, listInputFieldsPlugin, manifestPlugin, readManifestFromFile, registryPlugin, requestPlugin, runActionPlugin, toSnakeCase, toTitleCase };
@@ -36,8 +36,11 @@ describe("getApp plugin", () => {
36
36
  it("should pass validation with valid appKey", async () => {
37
37
  const sdk = createTestSdk();
38
38
  const context = sdk.getContext();
39
- // Mock the API client in context to respond to listApps implementation
40
- context.api.get = vi.fn().mockResolvedValue({
39
+ // Mock the API client to handle both resolveAppKeys and listApps calls
40
+ context.api.get = vi
41
+ .fn()
42
+ // First call: resolveAppKeys may call to resolve slug to implementation name
43
+ .mockResolvedValueOnce({
41
44
  results: [
42
45
  {
43
46
  id: "SlackCLIAPI@1.0.0",
@@ -45,6 +48,25 @@ describe("getApp plugin", () => {
45
48
  description: "Team communication",
46
49
  primary_color: "#4A154B",
47
50
  categories: ["communication"],
51
+ slug: "slack",
52
+ key: "SlackCLIAPI",
53
+ version: "1.0.0",
54
+ },
55
+ ],
56
+ next: null,
57
+ })
58
+ // Second call: listApps main API call
59
+ .mockResolvedValueOnce({
60
+ results: [
61
+ {
62
+ id: "SlackCLIAPI@1.0.0",
63
+ name: "Slack",
64
+ description: "Team communication",
65
+ primary_color: "#4A154B",
66
+ categories: ["communication"],
67
+ slug: "slack",
68
+ key: "SlackCLIAPI",
69
+ version: "1.0.0",
48
70
  },
49
71
  ],
50
72
  next: null,
@@ -62,8 +84,16 @@ describe("getApp plugin", () => {
62
84
  it("should throw ZapierAppNotFoundError when app does not exist", async () => {
63
85
  const sdk = createTestSdk();
64
86
  const context = sdk.getContext();
65
- // Mock API to return empty results (app not found)
66
- context.api.get = vi.fn().mockResolvedValue({
87
+ // Mock API to return empty results for both potential calls
88
+ context.api.get = vi
89
+ .fn()
90
+ // First call: resolveAppKeys may call to resolve slug
91
+ .mockResolvedValueOnce({
92
+ results: [],
93
+ next: null,
94
+ })
95
+ // Second call: listApps main API call
96
+ .mockResolvedValueOnce({
67
97
  results: [],
68
98
  next: null,
69
99
  });
@@ -76,8 +106,27 @@ describe("getApp plugin", () => {
76
106
  it("should return essential app properties", async () => {
77
107
  const sdk = createTestSdk();
78
108
  const context = sdk.getContext();
79
- // Mock the API client in context to respond to listApps implementation
80
- context.api.get = vi.fn().mockResolvedValue({
109
+ // Mock the API client to handle both resolveAppKeys and listApps calls
110
+ context.api.get = vi
111
+ .fn()
112
+ // First call: resolveAppKeys may call to resolve slug to implementation name
113
+ .mockResolvedValueOnce({
114
+ results: [
115
+ {
116
+ id: "TestCLIAPI@1.0.0",
117
+ name: "Test App",
118
+ description: "Test description",
119
+ primary_color: "#FF0000",
120
+ categories: ["testing"],
121
+ slug: "test",
122
+ key: "TestCLIAPI",
123
+ version: "1.0.0",
124
+ },
125
+ ],
126
+ next: null,
127
+ })
128
+ // Second call: listApps main API call
129
+ .mockResolvedValueOnce({
81
130
  results: [
82
131
  {
83
132
  id: "TestCLIAPI@1.0.0",
@@ -85,6 +134,9 @@ describe("getApp plugin", () => {
85
134
  description: "Test description",
86
135
  primary_color: "#FF0000",
87
136
  categories: ["testing"],
137
+ slug: "test",
138
+ key: "TestCLIAPI",
139
+ version: "1.0.0",
88
140
  },
89
141
  ],
90
142
  next: null,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/listApps/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,WAAW,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAWlD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC;QAC/C,IAAI,EAAE,OAAO,EAAE,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,GACA,aAAa,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG;QACxD,KAAK,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;KACjC,CAAC;IACJ,OAAO,EAAE;QACP,IAAI,EAAE;YACJ,QAAQ,EAAE;gBACR,WAAW,EAAE,OAAO,cAAc,CAAC;aACpC,CAAC;SACH,CAAC;KACH,CAAC;CACH;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CACjC,EAAE,EACF,cAAc,CAAC,iBAAiB,GAAG,sBAAsB,CAAC,EAC1D,sBAAsB,CA+GvB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/listApps/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,WAAW,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAWlD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC;QAC/C,IAAI,EAAE,OAAO,EAAE,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,GACA,aAAa,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG;QACxD,KAAK,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;KACjC,CAAC;IACJ,OAAO,EAAE;QACP,IAAI,EAAE;YACJ,QAAQ,EAAE;gBACR,WAAW,EAAE,OAAO,cAAc,CAAC;aACpC,CAAC;SACH,CAAC;KACH,CAAC;CACH;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CACjC,EAAE,EACF,cAAc,CAAC,iBAAiB,GAAG,sBAAsB,CAAC,EAC1D,sBAAsB,CAwHvB,CAAC"}
@@ -5,10 +5,10 @@ import { normalizeImplementationMetaToAppItem, splitVersionedKey, toAppLocator,
5
5
  import { extractCursor } from "../../utils/function-utils";
6
6
  export const listAppsPlugin = ({ context }) => {
7
7
  const listApps = createPaginatedFunction(async function listAppsPage(options) {
8
- const api = context.api;
9
- const opts = options;
10
- const appLocators = await context.resolveAppKeys({
11
- appKeys: [...(opts.appKeys ?? [])],
8
+ const { api, resolveAppKeys } = context;
9
+ const appKeys = options.appKeys ?? [];
10
+ const appLocators = await resolveAppKeys({
11
+ appKeys: [...appKeys],
12
12
  });
13
13
  const implementationNameToLocator = {};
14
14
  for (const locator of appLocators) {
@@ -25,9 +25,9 @@ export const listAppsPlugin = ({ context }) => {
25
25
  if (duplicatedLookupAppKeys.length > 0) {
26
26
  throw new Error(`Duplicate lookup app keys found: ${duplicatedLookupAppKeys.join(", ")}`);
27
27
  }
28
- if (opts.search) {
28
+ if (options.search) {
29
29
  const searchParams = {};
30
- searchParams.term = opts.search;
30
+ searchParams.term = options.search;
31
31
  const searchEnvelope = await api.get("/api/v4/implementations-meta/search/", {
32
32
  searchParams,
33
33
  });
@@ -45,18 +45,26 @@ export const listAppsPlugin = ({ context }) => {
45
45
  }
46
46
  }
47
47
  const searchParams = {};
48
- if (opts.pageSize) {
49
- searchParams.limit = opts.pageSize.toString();
48
+ if (options.pageSize) {
49
+ searchParams.limit = options.pageSize.toString();
50
50
  }
51
51
  if (appLocators.length === 0) {
52
52
  searchParams.latest_only = "true";
53
53
  }
54
- if (opts.cursor) {
55
- searchParams.offset = opts.cursor;
54
+ if (options.cursor) {
55
+ searchParams.offset = options.cursor;
56
56
  }
57
57
  searchParams.selected_apis = appLocators
58
58
  .map((locator) => toImplementationId(locator))
59
59
  .join(",");
60
+ // If we have app keys, but they resolved to an empty list, then we need to short-circuit and return an empty list.
61
+ // Otherwise, the API will return all apps, since no selected_apis are provided.
62
+ if (appKeys.length > 0 && appLocators.length === 0) {
63
+ return {
64
+ data: [],
65
+ nextCursor: undefined,
66
+ };
67
+ }
60
68
  const implementationsEnvelope = await api.get("/api/v4/implementations-meta/lookup/", {
61
69
  searchParams,
62
70
  });
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/schemas/App.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAMtE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBzB,CAAC;AAMF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/schemas/App.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAMtE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BzB,CAAC;AAMF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { withFormatter } from "../utils/schema-utils";
3
3
  import { ImplementationMetaSchema } from "../api/schemas";
4
+ import { toSnakeCase } from "../utils/string-utils";
4
5
  // ============================================================================
5
6
  // App Item Schema (extends ImplementationMetaSchema with transformed fields)
6
7
  // ============================================================================
@@ -10,10 +11,19 @@ export const AppItemSchema = withFormatter(ImplementationMetaSchema.omit({ name:
10
11
  implementation_id: z.string(), // Mapped from id (full versioned ID)
11
12
  }), {
12
13
  format: (item) => {
14
+ // Create additional keys if slug exists
15
+ const additionalKeys = [];
16
+ if (item.slug && item.slug !== item.key) {
17
+ additionalKeys.push(item.slug);
18
+ const snakeCaseSlug = toSnakeCase(item.slug);
19
+ if (snakeCaseSlug !== item.slug && snakeCaseSlug !== item.key) {
20
+ additionalKeys.push(snakeCaseSlug);
21
+ }
22
+ }
13
23
  return {
14
24
  title: item.title,
15
25
  key: item.key,
16
- keys: [item.slug, item.key].filter(Boolean),
26
+ keys: [item.key, ...additionalKeys],
17
27
  description: item.description,
18
28
  details: [],
19
29
  };