@zapier/zapier-sdk 0.8.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +17 -40
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +14 -0
- package/dist/api/debug.d.ts +1 -0
- package/dist/api/debug.d.ts.map +1 -1
- package/dist/api/debug.js +42 -1
- package/dist/api/debug.test.d.ts +2 -0
- package/dist/api/debug.test.d.ts.map +1 -0
- package/dist/api/debug.test.js +59 -0
- package/dist/api/schemas.d.ts +451 -251
- package/dist/api/schemas.d.ts.map +1 -1
- package/dist/api/schemas.js +51 -29
- package/dist/index.cjs +1149 -751
- package/dist/index.d.mts +2359 -2161
- package/dist/index.d.ts +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/index.mjs +1143 -743
- package/dist/plugins/apps/index.d.ts +4 -0
- package/dist/plugins/apps/index.d.ts.map +1 -1
- package/dist/plugins/findFirstAuthentication/index.d.ts +1 -1
- package/dist/plugins/findFirstAuthentication/index.d.ts.map +1 -1
- package/dist/plugins/findFirstAuthentication/index.js +9 -1
- package/dist/plugins/findFirstAuthentication/index.test.js +3 -4
- package/dist/plugins/findFirstAuthentication/schemas.d.ts +5 -3
- package/dist/plugins/findFirstAuthentication/schemas.d.ts.map +1 -1
- package/dist/plugins/findUniqueAuthentication/index.d.ts.map +1 -1
- package/dist/plugins/findUniqueAuthentication/index.js +4 -0
- package/dist/plugins/findUniqueAuthentication/schemas.d.ts +5 -3
- package/dist/plugins/findUniqueAuthentication/schemas.d.ts.map +1 -1
- package/dist/plugins/getAction/index.d.ts.map +1 -1
- package/dist/plugins/getAction/index.js +10 -0
- package/dist/plugins/getAction/schemas.d.ts +5 -3
- package/dist/plugins/getAction/schemas.d.ts.map +1 -1
- package/dist/plugins/getApp/index.d.ts +2 -7
- package/dist/plugins/getApp/index.d.ts.map +1 -1
- package/dist/plugins/getApp/index.js +17 -9
- package/dist/plugins/getApp/index.test.js +3 -3
- package/dist/plugins/getApp/schemas.d.ts +3 -1
- package/dist/plugins/getApp/schemas.d.ts.map +1 -1
- package/dist/plugins/getApp/schemas.js +2 -4
- package/dist/plugins/getAuthentication/index.d.ts.map +1 -1
- package/dist/plugins/getAuthentication/index.js +8 -0
- package/dist/plugins/getAuthentication/index.test.js +1 -1
- package/dist/plugins/getProfile/index.d.ts.map +1 -1
- package/dist/plugins/getProfile/index.js +4 -0
- package/dist/plugins/getProfile/schemas.d.ts.map +1 -1
- package/dist/plugins/getProfile/schemas.js +4 -3
- package/dist/plugins/listActions/index.d.ts +2 -4
- package/dist/plugins/listActions/index.d.ts.map +1 -1
- package/dist/plugins/listActions/index.js +10 -1
- package/dist/plugins/listActions/index.test.js +4 -4
- package/dist/plugins/listActions/schemas.d.ts +5 -3
- package/dist/plugins/listActions/schemas.d.ts.map +1 -1
- package/dist/plugins/listActions/schemas.js +2 -4
- package/dist/plugins/listApps/index.d.ts +4 -7
- package/dist/plugins/listApps/index.d.ts.map +1 -1
- package/dist/plugins/listApps/index.js +37 -17
- package/dist/plugins/listApps/index.test.js +23 -3
- package/dist/plugins/listApps/schemas.d.ts.map +1 -1
- package/dist/plugins/listApps/schemas.js +3 -9
- package/dist/plugins/listAuthentications/index.d.ts +2 -4
- package/dist/plugins/listAuthentications/index.d.ts.map +1 -1
- package/dist/plugins/listAuthentications/index.js +12 -0
- package/dist/plugins/listAuthentications/index.test.js +39 -13
- package/dist/plugins/listAuthentications/schemas.d.ts +8 -3
- package/dist/plugins/listAuthentications/schemas.d.ts.map +1 -1
- package/dist/plugins/listAuthentications/schemas.js +4 -0
- package/dist/plugins/listInputFieldChoices/index.d.ts.map +1 -1
- package/dist/plugins/listInputFieldChoices/index.js +14 -2
- package/dist/plugins/listInputFieldChoices/schemas.d.ts +5 -3
- package/dist/plugins/listInputFieldChoices/schemas.d.ts.map +1 -1
- package/dist/plugins/listInputFieldChoices/schemas.js +10 -19
- package/dist/plugins/listInputFields/index.d.ts.map +1 -1
- package/dist/plugins/listInputFields/index.js +14 -2
- package/dist/plugins/listInputFields/index.test.js +5 -9
- package/dist/plugins/listInputFields/schemas.d.ts +5 -3
- package/dist/plugins/listInputFields/schemas.d.ts.map +1 -1
- package/dist/plugins/manifest/index.d.ts +25 -9
- package/dist/plugins/manifest/index.d.ts.map +1 -1
- package/dist/plugins/manifest/index.js +239 -67
- package/dist/plugins/manifest/index.test.js +426 -171
- package/dist/plugins/manifest/schemas.d.ts +5 -1
- package/dist/plugins/manifest/schemas.d.ts.map +1 -1
- package/dist/plugins/manifest/schemas.js +1 -0
- package/dist/plugins/registry/index.d.ts.map +1 -1
- package/dist/plugins/registry/index.js +8 -2
- package/dist/plugins/request/index.d.ts.map +1 -1
- package/dist/plugins/request/index.js +1 -0
- package/dist/plugins/runAction/index.d.ts.map +1 -1
- package/dist/plugins/runAction/index.js +12 -0
- package/dist/plugins/runAction/schemas.d.ts +5 -3
- package/dist/plugins/runAction/schemas.d.ts.map +1 -1
- package/dist/resolvers/actionKey.d.ts +13 -7
- package/dist/resolvers/actionKey.d.ts.map +1 -1
- package/dist/resolvers/actionType.d.ts +8 -7
- package/dist/resolvers/actionType.d.ts.map +1 -1
- package/dist/resolvers/appKey.d.ts +2 -6
- package/dist/resolvers/appKey.d.ts.map +1 -1
- package/dist/resolvers/authenticationId.d.ts +7 -7
- package/dist/resolvers/authenticationId.d.ts.map +1 -1
- package/dist/resolvers/authenticationId.js +16 -7
- package/dist/resolvers/index.d.ts +3 -35
- package/dist/resolvers/index.d.ts.map +1 -1
- package/dist/resolvers/index.js +4 -87
- package/dist/resolvers/inputFieldKey.d.ts +11 -0
- package/dist/resolvers/inputFieldKey.d.ts.map +1 -0
- package/dist/resolvers/inputFieldKey.js +23 -0
- package/dist/resolvers/inputs.d.ts +11 -6
- package/dist/resolvers/inputs.d.ts.map +1 -1
- package/dist/resolvers/inputs.js +17 -0
- package/dist/schemas/Action.d.ts +8 -8
- package/dist/schemas/Action.d.ts.map +1 -1
- package/dist/schemas/Action.js +8 -3
- package/dist/schemas/App.d.ts +183 -11
- package/dist/schemas/App.d.ts.map +1 -1
- package/dist/schemas/App.js +7 -9
- package/dist/schemas/Auth.d.ts +12 -12
- package/dist/schemas/Auth.js +1 -1
- package/dist/schemas/Field.d.ts +5 -98
- package/dist/schemas/Field.d.ts.map +1 -1
- package/dist/schemas/Field.js +24 -52
- package/dist/schemas/Run.d.ts +3 -0
- package/dist/schemas/Run.d.ts.map +1 -0
- package/dist/schemas/Run.js +31 -0
- package/dist/schemas/UserProfile.d.ts +11 -11
- package/dist/schemas/UserProfile.d.ts.map +1 -1
- package/dist/schemas/UserProfile.js +21 -7
- package/dist/sdk.d.ts +15 -14
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +1 -4
- package/dist/types/plugin.d.ts +6 -0
- package/dist/types/plugin.d.ts.map +1 -1
- package/dist/types/properties.d.ts +3 -1
- package/dist/types/properties.d.ts.map +1 -1
- package/dist/types/sdk.d.ts +11 -3
- package/dist/types/sdk.d.ts.map +1 -1
- package/dist/utils/domain-utils.d.ts +17 -16
- package/dist/utils/domain-utils.d.ts.map +1 -1
- package/dist/utils/domain-utils.js +53 -78
- package/dist/utils/domain-utils.test.js +157 -3
- package/dist/utils/file-utils.d.ts +4 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +74 -0
- package/dist/utils/file-utils.test.d.ts +2 -0
- package/dist/utils/file-utils.test.d.ts.map +1 -0
- package/dist/utils/file-utils.test.js +51 -0
- package/dist/utils/schema-utils.d.ts +44 -21
- package/dist/utils/schema-utils.d.ts.map +1 -1
- package/dist/utils/schema-utils.js +17 -11
- package/package.json +1 -1
- package/src/api/client.ts +12 -0
- package/src/api/debug.test.ts +76 -0
- package/src/api/debug.ts +46 -2
- package/src/api/schemas.ts +51 -29
- package/src/index.ts +5 -6
- package/src/plugins/apps/index.ts +9 -2
- package/src/plugins/findFirstAuthentication/index.test.ts +8 -5
- package/src/plugins/findFirstAuthentication/index.ts +14 -2
- package/src/plugins/findUniqueAuthentication/index.ts +4 -0
- package/src/plugins/getAction/index.ts +14 -0
- package/src/plugins/getApp/index.test.ts +3 -3
- package/src/plugins/getApp/index.ts +20 -14
- package/src/plugins/getApp/schemas.ts +7 -12
- package/src/plugins/getAuthentication/index.test.ts +1 -1
- package/src/plugins/getAuthentication/index.ts +8 -0
- package/src/plugins/getProfile/index.ts +4 -0
- package/src/plugins/getProfile/schemas.ts +4 -6
- package/src/plugins/listActions/index.test.ts +8 -7
- package/src/plugins/listActions/index.ts +12 -3
- package/src/plugins/listActions/schemas.ts +20 -25
- package/src/plugins/listApps/index.test.ts +24 -3
- package/src/plugins/listApps/index.ts +50 -25
- package/src/plugins/listApps/schemas.ts +17 -26
- package/src/plugins/listAuthentications/index.test.ts +52 -15
- package/src/plugins/listAuthentications/index.ts +15 -2
- package/src/plugins/listAuthentications/schemas.ts +4 -0
- package/src/plugins/listInputFieldChoices/index.ts +21 -1
- package/src/plugins/listInputFieldChoices/schemas.ts +61 -76
- package/src/plugins/listInputFields/index.test.ts +5 -9
- package/src/plugins/listInputFields/index.ts +20 -2
- package/src/plugins/manifest/index.test.ts +503 -197
- package/src/plugins/manifest/index.ts +338 -82
- package/src/plugins/manifest/schemas.ts +9 -2
- package/src/plugins/registry/index.ts +8 -2
- package/src/plugins/request/index.ts +1 -0
- package/src/plugins/runAction/index.ts +18 -0
- package/src/resolvers/actionKey.ts +15 -13
- package/src/resolvers/actionType.ts +10 -12
- package/src/resolvers/appKey.ts +2 -6
- package/src/resolvers/authenticationId.ts +25 -19
- package/src/resolvers/index.ts +7 -113
- package/src/resolvers/inputFieldKey.ts +38 -0
- package/src/resolvers/inputs.ts +28 -10
- package/src/schemas/Action.ts +8 -3
- package/src/schemas/App.ts +7 -9
- package/src/schemas/Auth.ts +1 -1
- package/src/schemas/Field.ts +24 -57
- package/src/schemas/Run.ts +40 -0
- package/src/schemas/UserProfile.ts +24 -7
- package/src/sdk.ts +18 -12
- package/src/types/plugin.ts +8 -0
- package/src/types/sdk.ts +31 -21
- package/src/utils/domain-utils.test.ts +196 -2
- package/src/utils/domain-utils.ts +77 -102
- package/src/utils/file-utils.test.ts +73 -0
- package/src/utils/file-utils.ts +94 -0
- package/src/utils/schema-utils.ts +96 -44
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/plugins/lockVersion/index.d.ts +0 -24
- package/dist/plugins/lockVersion/index.d.ts.map +0 -1
- package/dist/plugins/lockVersion/index.js +0 -72
- package/dist/plugins/lockVersion/index.test.d.ts +0 -2
- package/dist/plugins/lockVersion/index.test.d.ts.map +0 -1
- package/dist/plugins/lockVersion/index.test.js +0 -129
- package/dist/plugins/lockVersion/schemas.d.ts +0 -10
- package/dist/plugins/lockVersion/schemas.d.ts.map +0 -1
- package/dist/plugins/lockVersion/schemas.js +0 -6
- package/src/plugins/lockVersion/index.test.ts +0 -176
- package/src/plugins/lockVersion/index.ts +0 -112
- package/src/plugins/lockVersion/schemas.ts +0 -9
package/src/types/plugin.ts
CHANGED
|
@@ -22,7 +22,12 @@ export interface PluginProvides extends Record<string, any> {
|
|
|
22
22
|
|
|
23
23
|
export interface PluginMeta {
|
|
24
24
|
categories: string[];
|
|
25
|
+
type?: "list" | "item";
|
|
26
|
+
itemType?: string;
|
|
27
|
+
returnType?: string;
|
|
25
28
|
inputSchema: z.ZodSchema;
|
|
29
|
+
outputSchema?: z.ZodSchema;
|
|
30
|
+
resolvers?: Record<string, any>;
|
|
26
31
|
[key: string]: any;
|
|
27
32
|
}
|
|
28
33
|
|
|
@@ -41,6 +46,9 @@ export type GetSdkType<TPluginProvides extends PluginProvides> =
|
|
|
41
46
|
getContext(): NonNullable<ExtractContextProperties<TPluginProvides>>;
|
|
42
47
|
};
|
|
43
48
|
|
|
49
|
+
export type GetContextType<TPluginProvides extends PluginProvides> =
|
|
50
|
+
NonNullable<ExtractContextProperties<TPluginProvides>>;
|
|
51
|
+
|
|
44
52
|
// Helper type for standard plugin options with SDK dependency and meta context
|
|
45
53
|
export type PluginOptions<TSdk = {}, TContext = {}> = {
|
|
46
54
|
sdk: TSdk;
|
package/src/types/sdk.ts
CHANGED
|
@@ -29,12 +29,12 @@ import type { GetAuthenticationSdkFunction } from "../plugins/getAuthentication/
|
|
|
29
29
|
import type { FindFirstAuthenticationSdkFunction } from "../plugins/findFirstAuthentication/schemas";
|
|
30
30
|
import type { FindUniqueAuthenticationSdkFunction } from "../plugins/findUniqueAuthentication/schemas";
|
|
31
31
|
import type { RelayRequestSdkFunction } from "../plugins/request/schemas";
|
|
32
|
-
import type { LockVersionPluginProvides } from "../plugins/lockVersion";
|
|
33
32
|
|
|
34
33
|
import type { z } from "zod";
|
|
35
34
|
import type { RegistryPluginProvides } from "../plugins/registry";
|
|
36
35
|
import type { GetProfilePluginProvides } from "../plugins/getProfile";
|
|
37
|
-
import type { AppsPluginProvides } from "../plugins/apps";
|
|
36
|
+
import type { AppsPluginProvides, ZapierSdkApps } from "../plugins/apps";
|
|
37
|
+
import type { ActionProxy } from "../plugins/apps/types";
|
|
38
38
|
import type { FetchPluginProvides } from "../plugins/fetch";
|
|
39
39
|
import type { ListAppsPluginProvides } from "../plugins/listApps";
|
|
40
40
|
import type { GetAppPluginProvides } from "../plugins/getApp";
|
|
@@ -46,6 +46,7 @@ import type { GetAuthenticationPluginProvides } from "../plugins/getAuthenticati
|
|
|
46
46
|
import type { FindFirstAuthenticationPluginProvides } from "../plugins/findFirstAuthentication";
|
|
47
47
|
import type { FindUniqueAuthenticationPluginProvides } from "../plugins/findUniqueAuthentication";
|
|
48
48
|
import type { ListInputFieldsPluginProvides } from "../plugins/listInputFields";
|
|
49
|
+
import type { ListInputFieldChoicesPluginProvides } from "../plugins/listInputFieldChoices";
|
|
49
50
|
import type { RequestPluginProvides } from "../plugins/request";
|
|
50
51
|
import type { GetSdkType } from "./plugin";
|
|
51
52
|
import type { ManifestPluginProvides } from "../plugins/manifest";
|
|
@@ -56,8 +57,13 @@ import type { ManifestPluginProvides } from "../plugins/manifest";
|
|
|
56
57
|
// Registry entry interface with proper types
|
|
57
58
|
export interface FunctionRegistryEntry {
|
|
58
59
|
name: string;
|
|
60
|
+
type?: "list" | "item";
|
|
61
|
+
itemType?: string;
|
|
62
|
+
returnType?: string;
|
|
59
63
|
inputSchema: z.ZodSchema;
|
|
64
|
+
outputSchema?: z.ZodSchema;
|
|
60
65
|
categories: string[];
|
|
66
|
+
resolvers?: Record<string, any>;
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
// Compose SDK functions from individual function interfaces
|
|
@@ -78,22 +84,26 @@ export interface ZapierSdkFunctions
|
|
|
78
84
|
// Note: ZapierSdk type removed - use ReturnType<typeof createZapierSdk> instead
|
|
79
85
|
// This ensures the type matches exactly what the builder produces
|
|
80
86
|
|
|
81
|
-
export
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
87
|
+
export interface ZapierSdk
|
|
88
|
+
extends GetSdkType<
|
|
89
|
+
RegistryPluginProvides &
|
|
90
|
+
FetchPluginProvides &
|
|
91
|
+
AppsPluginProvides &
|
|
92
|
+
ListAppsPluginProvides &
|
|
93
|
+
ManifestPluginProvides &
|
|
94
|
+
GetAppPluginProvides &
|
|
95
|
+
ListActionsPluginProvides &
|
|
96
|
+
GetActionPluginProvides &
|
|
97
|
+
RunActionPluginProvides &
|
|
98
|
+
ListAuthenticationsPluginProvides &
|
|
99
|
+
GetAuthenticationPluginProvides &
|
|
100
|
+
FindFirstAuthenticationPluginProvides &
|
|
101
|
+
FindUniqueAuthenticationPluginProvides &
|
|
102
|
+
ListInputFieldsPluginProvides &
|
|
103
|
+
ListInputFieldChoicesPluginProvides &
|
|
104
|
+
RequestPluginProvides &
|
|
105
|
+
GetProfilePluginProvides
|
|
106
|
+
> {
|
|
107
|
+
// Override apps property to intersect ActionProxy with our typed apps
|
|
108
|
+
apps: ActionProxy & ZapierSdkApps;
|
|
109
|
+
}
|
|
@@ -2,6 +2,7 @@ import { describe, it, expect } from "vitest";
|
|
|
2
2
|
import {
|
|
3
3
|
groupVersionedAppKeysByType,
|
|
4
4
|
groupAppKeysByType,
|
|
5
|
+
toAppLocator,
|
|
5
6
|
} from "./domain-utils";
|
|
6
7
|
|
|
7
8
|
describe("domain-utils", () => {
|
|
@@ -134,11 +135,13 @@ describe("domain-utils", () => {
|
|
|
134
135
|
"FormatterCLIAPI@1.0.0",
|
|
135
136
|
"slack@2.1.0", // exact duplicate
|
|
136
137
|
"FormatterCLIAPI@1.0.0", // exact duplicate
|
|
138
|
+
"slack@2.1.1",
|
|
139
|
+
"FormatterCLIAPI@1.0.1",
|
|
137
140
|
]);
|
|
138
141
|
|
|
139
142
|
expect(result).toEqual({
|
|
140
|
-
selectedApi: ["FormatterCLIAPI@1.0.0"],
|
|
141
|
-
slug: ["slack@2.1.0"],
|
|
143
|
+
selectedApi: ["FormatterCLIAPI@1.0.0", "FormatterCLIAPI@1.0.1"],
|
|
144
|
+
slug: ["slack@2.1.0", "slack@2.1.1"],
|
|
142
145
|
});
|
|
143
146
|
});
|
|
144
147
|
});
|
|
@@ -236,4 +239,195 @@ describe("domain-utils", () => {
|
|
|
236
239
|
});
|
|
237
240
|
});
|
|
238
241
|
});
|
|
242
|
+
|
|
243
|
+
describe("toAppLocator", () => {
|
|
244
|
+
it("should reject UUID app keys", () => {
|
|
245
|
+
expect(() => {
|
|
246
|
+
toAppLocator("61e47557-af91-4b0c-a3e0-c28606357664");
|
|
247
|
+
}).toThrow(
|
|
248
|
+
"UUID app keys are not supported. Use app slug or implementation ID instead of: 61e47557-af91-4b0c-a3e0-c28606357664",
|
|
249
|
+
);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("should reject UUID app keys with versions", () => {
|
|
253
|
+
expect(() => {
|
|
254
|
+
toAppLocator("61e47557-af91-4b0c-a3e0-c28606357664@1.0.0");
|
|
255
|
+
}).toThrow(
|
|
256
|
+
"UUID app keys are not supported. Use app slug or implementation ID instead of: 61e47557-af91-4b0c-a3e0-c28606357664@1.0.0",
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("should reject uppercase UUID app keys", () => {
|
|
261
|
+
expect(() => {
|
|
262
|
+
toAppLocator("61E47557-AF91-4B0C-A3E0-C28606357664");
|
|
263
|
+
}).toThrow(
|
|
264
|
+
"UUID app keys are not supported. Use app slug or implementation ID instead of: 61E47557-AF91-4B0C-A3E0-C28606357664",
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("should handle simple slug without version", () => {
|
|
269
|
+
const result = toAppLocator("slack");
|
|
270
|
+
|
|
271
|
+
expect(result).toEqual({
|
|
272
|
+
lookupAppKey: "slack",
|
|
273
|
+
slug: "slack",
|
|
274
|
+
implementationName: undefined,
|
|
275
|
+
version: undefined,
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("should handle slug with version", () => {
|
|
280
|
+
const result = toAppLocator("slack@1.0.0");
|
|
281
|
+
|
|
282
|
+
expect(result).toEqual({
|
|
283
|
+
lookupAppKey: "slack",
|
|
284
|
+
slug: "slack",
|
|
285
|
+
implementationName: undefined,
|
|
286
|
+
version: "1.0.0",
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("should handle dashified slug", () => {
|
|
291
|
+
const result = toAppLocator("google-sheets");
|
|
292
|
+
|
|
293
|
+
expect(result).toEqual({
|
|
294
|
+
lookupAppKey: "google-sheets",
|
|
295
|
+
slug: "google-sheets",
|
|
296
|
+
implementationName: undefined,
|
|
297
|
+
version: undefined,
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it("should handle dashified slug with version", () => {
|
|
302
|
+
const result = toAppLocator("google-sheets@2.1.3");
|
|
303
|
+
|
|
304
|
+
expect(result).toEqual({
|
|
305
|
+
lookupAppKey: "google-sheets",
|
|
306
|
+
slug: "google-sheets",
|
|
307
|
+
implementationName: undefined,
|
|
308
|
+
version: "2.1.3",
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("should handle snake_cased slug and convert to dash", () => {
|
|
313
|
+
const result = toAppLocator("google_sheets");
|
|
314
|
+
|
|
315
|
+
expect(result).toEqual({
|
|
316
|
+
lookupAppKey: "google_sheets",
|
|
317
|
+
slug: "google-sheets",
|
|
318
|
+
implementationName: undefined,
|
|
319
|
+
version: undefined,
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("should handle snake_cased slug with version and convert to dash", () => {
|
|
324
|
+
const result = toAppLocator("google_sheets@2.1.3");
|
|
325
|
+
|
|
326
|
+
expect(result).toEqual({
|
|
327
|
+
lookupAppKey: "google_sheets",
|
|
328
|
+
slug: "google-sheets",
|
|
329
|
+
implementationName: undefined,
|
|
330
|
+
version: "2.1.3",
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it("should handle leading underscore snake_cased slug", () => {
|
|
335
|
+
const result = toAppLocator("_100hires_ats");
|
|
336
|
+
|
|
337
|
+
expect(result).toEqual({
|
|
338
|
+
lookupAppKey: "_100hires_ats",
|
|
339
|
+
slug: "100hires-ats",
|
|
340
|
+
implementationName: undefined,
|
|
341
|
+
version: undefined,
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it("should handle leading underscore snake_cased slug with version", () => {
|
|
346
|
+
const result = toAppLocator("_100hires_ats@3.0.0");
|
|
347
|
+
|
|
348
|
+
expect(result).toEqual({
|
|
349
|
+
lookupAppKey: "_100hires_ats",
|
|
350
|
+
slug: "100hires-ats",
|
|
351
|
+
implementationName: undefined,
|
|
352
|
+
version: "3.0.0",
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("should handle implementation name without version", () => {
|
|
357
|
+
const result = toAppLocator("SlackCLIAPI");
|
|
358
|
+
|
|
359
|
+
expect(result).toEqual({
|
|
360
|
+
lookupAppKey: "SlackCLIAPI",
|
|
361
|
+
slug: undefined,
|
|
362
|
+
implementationName: "SlackCLIAPI",
|
|
363
|
+
version: undefined,
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("should handle implementation name with version", () => {
|
|
368
|
+
const result = toAppLocator("SlackCLIAPI@1.29.0");
|
|
369
|
+
|
|
370
|
+
expect(result).toEqual({
|
|
371
|
+
lookupAppKey: "SlackCLIAPI",
|
|
372
|
+
slug: undefined,
|
|
373
|
+
implementationName: "SlackCLIAPI",
|
|
374
|
+
version: "1.29.0",
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("should handle edge cases with complex version numbers", () => {
|
|
379
|
+
const result = toAppLocator("slack@1.2.3-beta.4+build.5");
|
|
380
|
+
|
|
381
|
+
expect(result).toEqual({
|
|
382
|
+
lookupAppKey: "slack",
|
|
383
|
+
slug: "slack",
|
|
384
|
+
implementationName: undefined,
|
|
385
|
+
version: "1.2.3-beta.4+build.5",
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it("should handle single character app keys", () => {
|
|
390
|
+
const result = toAppLocator("a");
|
|
391
|
+
|
|
392
|
+
expect(result).toEqual({
|
|
393
|
+
lookupAppKey: "a",
|
|
394
|
+
slug: "a",
|
|
395
|
+
implementationName: undefined,
|
|
396
|
+
version: undefined,
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it("should handle app keys with numbers", () => {
|
|
401
|
+
const result = toAppLocator("app123");
|
|
402
|
+
|
|
403
|
+
expect(result).toEqual({
|
|
404
|
+
lookupAppKey: "app123",
|
|
405
|
+
slug: "app123",
|
|
406
|
+
implementationName: undefined,
|
|
407
|
+
version: undefined,
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it("should handle mixed case app keys that aren't implementation names", () => {
|
|
412
|
+
const result = toAppLocator("SlackBot");
|
|
413
|
+
|
|
414
|
+
expect(result).toEqual({
|
|
415
|
+
lookupAppKey: "SlackBot",
|
|
416
|
+
slug: undefined,
|
|
417
|
+
implementationName: "SlackBot",
|
|
418
|
+
version: undefined,
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it("should handle empty version (app key ending with @)", () => {
|
|
423
|
+
const result = toAppLocator("slack@");
|
|
424
|
+
|
|
425
|
+
expect(result).toEqual({
|
|
426
|
+
lookupAppKey: "slack",
|
|
427
|
+
slug: "slack",
|
|
428
|
+
implementationName: undefined,
|
|
429
|
+
version: "",
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
});
|
|
239
433
|
});
|
|
@@ -2,14 +2,7 @@
|
|
|
2
2
|
* Domain utility functions for working with app identifiers and data normalization
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
Action,
|
|
7
|
-
App,
|
|
8
|
-
Authentication,
|
|
9
|
-
Implementation,
|
|
10
|
-
ImplementationMeta,
|
|
11
|
-
Service,
|
|
12
|
-
} from "../api/types";
|
|
5
|
+
import type { Action, Authentication, ImplementationMeta } from "../api/types";
|
|
13
6
|
import type { ActionItem, AppItem, AuthenticationItem } from "../types/domain";
|
|
14
7
|
|
|
15
8
|
/**
|
|
@@ -34,48 +27,6 @@ export function splitVersionedKey(
|
|
|
34
27
|
return [versionedKey, undefined];
|
|
35
28
|
}
|
|
36
29
|
|
|
37
|
-
/**
|
|
38
|
-
* Converts an API App to an AppItem with normalized field names
|
|
39
|
-
*
|
|
40
|
-
* @param app - Raw App from API
|
|
41
|
-
* @returns Normalized AppItem
|
|
42
|
-
*/
|
|
43
|
-
export function normalizeAppItem(app: App): AppItem {
|
|
44
|
-
// Extract API name and version from current_implementation_id
|
|
45
|
-
const [apiName, appVersion] = app.current_implementation_id
|
|
46
|
-
? splitVersionedKey(app.current_implementation_id)
|
|
47
|
-
: [app.current_implementation_id || "", undefined];
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
title: app.name,
|
|
51
|
-
key: app.slug || apiName,
|
|
52
|
-
current_implementation_id: app.current_implementation_id || "",
|
|
53
|
-
version: appVersion, // Extract version separately
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Converts an Implementation to an AppItem with normalized field names
|
|
59
|
-
*
|
|
60
|
-
* @param implementation - Raw Implementation from API
|
|
61
|
-
* @returns Normalized AppItem
|
|
62
|
-
*/
|
|
63
|
-
export function normalizeImplementationToAppItem(
|
|
64
|
-
implementation: Implementation,
|
|
65
|
-
): AppItem {
|
|
66
|
-
// Extract API name and version from selected_api
|
|
67
|
-
const [selectedApi, appVersion] = implementation.selected_api
|
|
68
|
-
? splitVersionedKey(implementation.selected_api)
|
|
69
|
-
: [implementation.selected_api || "", undefined];
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
title: implementation.name || selectedApi,
|
|
73
|
-
key: selectedApi,
|
|
74
|
-
current_implementation_id: implementation.selected_api || "",
|
|
75
|
-
version: appVersion, // Extract version separately
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
30
|
/**
|
|
80
31
|
* Converts a lightweight ImplementationMeta to a slimmed AppItem
|
|
81
32
|
*
|
|
@@ -88,26 +39,17 @@ export function normalizeImplementationMetaToAppItem(
|
|
|
88
39
|
// Extract API name and version from the implementation ID
|
|
89
40
|
const [selectedApi, appVersion] = splitVersionedKey(implementationMeta.id);
|
|
90
41
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
key: selectedApi,
|
|
94
|
-
current_implementation_id: implementationMeta.id, // Keep the full versioned ID
|
|
95
|
-
version: appVersion, // Extract version separately
|
|
96
|
-
slug: implementationMeta.slug,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function normalizeServiceToAppItem(service: Service): AppItem {
|
|
101
|
-
const [selectedApi, appVersion] = service.current_implementation_id
|
|
102
|
-
? splitVersionedKey(service.current_implementation_id)
|
|
103
|
-
: ["", undefined];
|
|
42
|
+
// Destructure to exclude id and name before spreading
|
|
43
|
+
const { id, name, ...restOfImplementationMeta } = implementationMeta;
|
|
104
44
|
|
|
105
45
|
return {
|
|
106
|
-
|
|
46
|
+
// Pass through all ImplementationMeta fields except id and name
|
|
47
|
+
...restOfImplementationMeta,
|
|
48
|
+
// Transform key fields
|
|
49
|
+
title: name,
|
|
107
50
|
key: selectedApi,
|
|
108
|
-
|
|
109
|
-
version: appVersion,
|
|
110
|
-
description: service.description,
|
|
51
|
+
implementation_id: id, // Keep the full versioned ID
|
|
52
|
+
version: appVersion, // Extract version separately
|
|
111
53
|
};
|
|
112
54
|
}
|
|
113
55
|
|
|
@@ -204,40 +146,23 @@ export function groupVersionedAppKeysByType(appKeys: string[]): {
|
|
|
204
146
|
const seenSlugs = new Set<string>();
|
|
205
147
|
|
|
206
148
|
for (const key of appKeys) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (!seenSlugs.has(slugWithVersion)) {
|
|
224
|
-
seenSlugs.add(slugWithVersion);
|
|
225
|
-
result.slug.push(slugWithVersion); // Preserve full key including version
|
|
149
|
+
const appLocator = toAppLocator(key);
|
|
150
|
+
|
|
151
|
+
if (appLocator.slug) {
|
|
152
|
+
// For slugs, we need to reconstruct the versioned slug
|
|
153
|
+
const versionedSlug = appLocator.version
|
|
154
|
+
? `${appLocator.slug}@${appLocator.version}`
|
|
155
|
+
: appLocator.slug;
|
|
156
|
+
if (!seenSlugs.has(versionedSlug)) {
|
|
157
|
+
seenSlugs.add(versionedSlug);
|
|
158
|
+
result.slug.push(versionedSlug);
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
// For implementation names (selectedApi)
|
|
162
|
+
if (!seenSelectedApi.has(key)) {
|
|
163
|
+
seenSelectedApi.add(key);
|
|
164
|
+
result.selectedApi.push(key);
|
|
226
165
|
}
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Check if it's a slug (lowercase and dashes)
|
|
231
|
-
if (keyWithoutVersion.match(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)) {
|
|
232
|
-
seenSlugs.add(key);
|
|
233
|
-
result.slug.push(key);
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Everything else is a selected_api
|
|
238
|
-
if (!seenSelectedApi.has(key)) {
|
|
239
|
-
seenSelectedApi.add(key);
|
|
240
|
-
result.selectedApi.push(key); // Preserve full key including version
|
|
241
166
|
}
|
|
242
167
|
}
|
|
243
168
|
|
|
@@ -259,7 +184,11 @@ export function groupAppKeysByType(appKeys: string[]): {
|
|
|
259
184
|
};
|
|
260
185
|
}
|
|
261
186
|
|
|
262
|
-
function
|
|
187
|
+
export function isSlug(slug: string): boolean {
|
|
188
|
+
return !!slug.match(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function isSnakeCasedSlug(slug: string): boolean {
|
|
263
192
|
// Allow leading underscore for slugs starting with a number.
|
|
264
193
|
if (slug.match(/^_[0-9]/)) {
|
|
265
194
|
slug = slug.slice(1);
|
|
@@ -268,7 +197,7 @@ function isSnakeCasedSlug(slug: string): boolean {
|
|
|
268
197
|
return !!slug.match(/^[a-z0-9]+(?:_[a-z0-9]+)*$/);
|
|
269
198
|
}
|
|
270
199
|
|
|
271
|
-
function dashifySnakeCasedSlug(slug: string): string {
|
|
200
|
+
export function dashifySnakeCasedSlug(slug: string): string {
|
|
272
201
|
// Only dashify if it's a valid snake_cased slug.
|
|
273
202
|
if (!isSnakeCasedSlug(slug)) {
|
|
274
203
|
return slug;
|
|
@@ -281,3 +210,49 @@ function dashifySnakeCasedSlug(slug: string): string {
|
|
|
281
210
|
|
|
282
211
|
return slug.replace(/_/g, "-");
|
|
283
212
|
}
|
|
213
|
+
|
|
214
|
+
export interface AppLocator {
|
|
215
|
+
lookupAppKey: string;
|
|
216
|
+
slug?: string;
|
|
217
|
+
implementationName?: string;
|
|
218
|
+
version?: string;
|
|
219
|
+
}
|
|
220
|
+
export interface ResolvedAppLocator extends AppLocator {
|
|
221
|
+
implementationName: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function isUuid(appKey: string): boolean {
|
|
225
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
|
|
226
|
+
appKey,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function toAppLocator(appKey: string): AppLocator {
|
|
231
|
+
const [appKeyWithoutVersion, version] = splitVersionedKey(appKey);
|
|
232
|
+
if (isUuid(appKeyWithoutVersion)) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`UUID app keys are not supported. Use app slug or implementation ID instead of: ${appKey}`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
const slug = isSlug(appKeyWithoutVersion)
|
|
238
|
+
? appKeyWithoutVersion
|
|
239
|
+
: isSnakeCasedSlug(appKeyWithoutVersion)
|
|
240
|
+
? dashifySnakeCasedSlug(appKeyWithoutVersion)
|
|
241
|
+
: undefined;
|
|
242
|
+
return {
|
|
243
|
+
lookupAppKey: appKeyWithoutVersion,
|
|
244
|
+
slug,
|
|
245
|
+
implementationName: slug ? undefined : appKeyWithoutVersion,
|
|
246
|
+
version,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function isResolvedAppLocator(
|
|
251
|
+
appLocator: AppLocator,
|
|
252
|
+
): appLocator is ResolvedAppLocator {
|
|
253
|
+
return !!appLocator.implementationName;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function toImplementationId(appLocator: ResolvedAppLocator): string {
|
|
257
|
+
return `${appLocator.implementationName}@${appLocator.version || "latest"}`;
|
|
258
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { resolve, writeFile, readFile } from "./file-utils";
|
|
3
|
+
|
|
4
|
+
// Mock the dynamic imports to simulate missing modules
|
|
5
|
+
vi.mock("fs/promises", () => {
|
|
6
|
+
throw new Error("Module not found");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
vi.mock("path", () => {
|
|
10
|
+
throw new Error("Module not found");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe("file-utils", () => {
|
|
14
|
+
describe("resolve", () => {
|
|
15
|
+
it("should handle absolute paths", async () => {
|
|
16
|
+
expect(await resolve("/absolute/path")).toBe("/absolute/path");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should handle relative paths with ./", async () => {
|
|
20
|
+
expect(await resolve("./relative/path")).toBe("/relative/path");
|
|
21
|
+
expect(await resolve("./relative/path", "/base")).toBe(
|
|
22
|
+
"/base/relative/path",
|
|
23
|
+
);
|
|
24
|
+
expect(await resolve("./relative/path", "/base/")).toBe(
|
|
25
|
+
"/base/relative/path",
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should handle parent directory paths with ../", async () => {
|
|
30
|
+
expect(await resolve("../parent/path")).toBe("/parent/path");
|
|
31
|
+
expect(await resolve("../../nested/parent/path")).toBe(
|
|
32
|
+
"/nested/parent/path",
|
|
33
|
+
);
|
|
34
|
+
expect(await resolve("../parent/path", "/base")).toBe(
|
|
35
|
+
"/base/parent/path",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should handle paths without prefix", async () => {
|
|
40
|
+
expect(await resolve("simple/path")).toBe("/simple/path");
|
|
41
|
+
expect(await resolve("simple/path", "/base")).toBe("/base/simple/path");
|
|
42
|
+
expect(await resolve("simple/path", "/base/")).toBe("/base/simple/path");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("writeFile and readFile with in-memory filesystem", () => {
|
|
47
|
+
it("should write and read files using in-memory filesystem when fs is not available", async () => {
|
|
48
|
+
const filePath = "/test/file.txt";
|
|
49
|
+
const content = "Hello, world!";
|
|
50
|
+
|
|
51
|
+
await writeFile(filePath, content);
|
|
52
|
+
|
|
53
|
+
const readContent = await readFile(filePath);
|
|
54
|
+
expect(readContent).toBe(content);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should throw error when reading non-existent file", async () => {
|
|
58
|
+
await expect(readFile("/non/existent/file.txt")).rejects.toThrow(
|
|
59
|
+
"File not found: /non/existent/file.txt",
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should handle multiple files in in-memory filesystem", async () => {
|
|
64
|
+
await writeFile("/file1.txt", "content1");
|
|
65
|
+
await writeFile("/file2.txt", "content2");
|
|
66
|
+
await writeFile("/dir/file3.txt", "content3");
|
|
67
|
+
|
|
68
|
+
expect(await readFile("/file1.txt")).toBe("content1");
|
|
69
|
+
expect(await readFile("/file2.txt")).toBe("content2");
|
|
70
|
+
expect(await readFile("/dir/file3.txt")).toBe("content3");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|