sharetribe-flex-build-sdk 1.15.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.
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Process management functions
3
+ *
4
+ * Programmatic API for managing Sharetribe transaction processes
5
+ */
6
+
7
+ import { apiGet, apiPost, apiPostMultipart, apiPostTransit, type MultipartField } from './api/client.js';
8
+ import { keyword, keywordMap } from './api/transit.js';
9
+
10
+ export interface ProcessListItem {
11
+ name: string;
12
+ version?: number;
13
+ }
14
+
15
+ export interface ProcessVersion {
16
+ createdAt: string;
17
+ version: number;
18
+ aliases?: string[];
19
+ transactionCount?: number;
20
+ }
21
+
22
+ export interface ProcessDetails {
23
+ definition: string;
24
+ version: number;
25
+ name: string;
26
+ emailTemplates?: Array<{
27
+ name: string;
28
+ html: string;
29
+ subject: string;
30
+ }>;
31
+ }
32
+
33
+ export interface CreateProcessResult {
34
+ name: string;
35
+ version: number;
36
+ }
37
+
38
+ export interface PushProcessResult {
39
+ version?: number;
40
+ noChanges?: boolean;
41
+ }
42
+
43
+ export interface AliasResult {
44
+ alias: string;
45
+ version: number;
46
+ }
47
+
48
+ /**
49
+ * Lists all processes for a marketplace
50
+ *
51
+ * @param apiKey - Optional Sharetribe API key. If not provided, reads from ~/.config/flex-cli/auth.edn
52
+ * @param marketplace - Marketplace ID
53
+ * @returns Array of processes with their latest versions
54
+ */
55
+ export async function listProcesses(
56
+ apiKey: string | undefined,
57
+ marketplace: string
58
+ ): Promise<ProcessListItem[]> {
59
+ const response = await apiGet<{ data: Array<{ 'process/name': string; 'process/version'?: number }> }>(
60
+ apiKey,
61
+ '/processes/query',
62
+ { marketplace }
63
+ );
64
+
65
+ return response.data.map(p => ({
66
+ name: p['process/name'],
67
+ version: p['process/version'],
68
+ }));
69
+ }
70
+
71
+ /**
72
+ * Lists all versions of a specific process
73
+ *
74
+ * @param apiKey - Sharetribe API key
75
+ * @param marketplace - Marketplace ID
76
+ * @param processName - Name of the process
77
+ * @returns Array of process versions
78
+ */
79
+ export async function listProcessVersions(
80
+ apiKey: string | undefined,
81
+ marketplace: string,
82
+ processName: string
83
+ ): Promise<ProcessVersion[]> {
84
+ const response = await apiGet<{
85
+ data: Array<{
86
+ 'process/createdAt': string;
87
+ 'process/version': number;
88
+ 'process/aliases'?: string[];
89
+ 'process/transactionCount'?: number;
90
+ }>;
91
+ }>(
92
+ apiKey,
93
+ '/processes/query-versions',
94
+ { marketplace, name: processName }
95
+ );
96
+
97
+ return response.data.map(v => ({
98
+ createdAt: v['process/createdAt'],
99
+ version: v['process/version'],
100
+ aliases: v['process/aliases'],
101
+ transactionCount: v['process/transactionCount'],
102
+ }));
103
+ }
104
+
105
+ /**
106
+ * Gets details of a specific process version
107
+ *
108
+ * @param apiKey - Sharetribe API key
109
+ * @param marketplace - Marketplace ID
110
+ * @param processName - Name of the process
111
+ * @param options - Optional version or alias to retrieve
112
+ * @returns Process details including definition and templates
113
+ */
114
+ export async function getProcess(
115
+ apiKey: string | undefined,
116
+ marketplace: string,
117
+ processName: string,
118
+ options?: { version?: string; alias?: string }
119
+ ): Promise<ProcessDetails> {
120
+ const queryParams: Record<string, string> = {
121
+ marketplace,
122
+ name: processName,
123
+ };
124
+
125
+ if (options?.version) {
126
+ queryParams.version = options.version;
127
+ } else if (options?.alias) {
128
+ queryParams.alias = options.alias;
129
+ }
130
+
131
+ const response = await apiGet<{ data: any }>(apiKey, '/processes/show', queryParams);
132
+
133
+ const emailTemplates = (response.data['process/emailTemplates'] || []).map((t: any) => ({
134
+ name: t['emailTemplate/name'],
135
+ html: t['emailTemplate/html'],
136
+ subject: t['emailTemplate/subject'],
137
+ }));
138
+
139
+ return {
140
+ definition: response.data['process/process'] || response.data.definition,
141
+ version: response.data['process/version'] || response.data.version,
142
+ name: response.data['process/name'] || processName,
143
+ emailTemplates,
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Creates a new transaction process
149
+ *
150
+ * @param apiKey - Sharetribe API key
151
+ * @param marketplace - Marketplace ID
152
+ * @param processName - Name for the new process
153
+ * @param definition - Process definition (EDN format)
154
+ * @returns Created process details
155
+ */
156
+ export async function createProcess(
157
+ apiKey: string | undefined,
158
+ marketplace: string,
159
+ processName: string,
160
+ definition: string
161
+ ): Promise<CreateProcessResult> {
162
+ const response = await apiPost<{ data: { 'process/name': string; 'process/version': number } }>(
163
+ apiKey,
164
+ '/processes/create',
165
+ { marketplace },
166
+ { name: processName, definition }
167
+ );
168
+
169
+ return {
170
+ name: response.data['process/name'],
171
+ version: response.data['process/version'],
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Pushes a new version of an existing process
177
+ *
178
+ * @param apiKey - Sharetribe API key
179
+ * @param marketplace - Marketplace ID
180
+ * @param processName - Name of the process
181
+ * @param definition - Process definition (EDN format)
182
+ * @param templates - Optional email templates
183
+ * @returns Push result with version number
184
+ */
185
+ export async function pushProcess(
186
+ apiKey: string | undefined,
187
+ marketplace: string,
188
+ processName: string,
189
+ definition: string,
190
+ templates?: Array<{ name: string; html: string; subject: string }>
191
+ ): Promise<PushProcessResult> {
192
+ const fields: MultipartField[] = [
193
+ { name: 'name', value: processName },
194
+ { name: 'definition', value: definition },
195
+ ];
196
+
197
+ if (templates) {
198
+ for (const template of templates) {
199
+ fields.push({ name: `template-html-${template.name}`, value: template.html });
200
+ fields.push({ name: `template-subject-${template.name}`, value: template.subject });
201
+ }
202
+ }
203
+
204
+ const response = await apiPostMultipart<{ data: any; meta?: { result?: string } }>(
205
+ apiKey,
206
+ '/processes/create-version',
207
+ { marketplace },
208
+ fields
209
+ );
210
+
211
+ if (response.meta?.result === 'no-changes') {
212
+ return { noChanges: true };
213
+ }
214
+
215
+ return {
216
+ version: response.data['process/version'] || response.data.version,
217
+ };
218
+ }
219
+
220
+ /**
221
+ * Creates a process alias
222
+ *
223
+ * @param apiKey - Sharetribe API key
224
+ * @param marketplace - Marketplace ID
225
+ * @param processName - Name of the process
226
+ * @param version - Version number to point the alias to
227
+ * @param alias - Alias name
228
+ * @returns Created alias details
229
+ */
230
+ export async function createAlias(
231
+ apiKey: string | undefined,
232
+ marketplace: string,
233
+ processName: string,
234
+ version: number,
235
+ alias: string
236
+ ): Promise<AliasResult> {
237
+ const response = await apiPostTransit<{
238
+ data: { 'processAlias/alias': string; 'processAlias/version': number };
239
+ }>(
240
+ apiKey,
241
+ '/aliases/create-alias',
242
+ { marketplace },
243
+ keywordMap({
244
+ name: keyword(processName),
245
+ version,
246
+ alias: keyword(alias),
247
+ })
248
+ );
249
+
250
+ return {
251
+ alias: response.data['processAlias/alias'],
252
+ version: response.data['processAlias/version'],
253
+ };
254
+ }
255
+
256
+ /**
257
+ * Updates a process alias to point to a different version
258
+ *
259
+ * @param apiKey - Sharetribe API key
260
+ * @param marketplace - Marketplace ID
261
+ * @param processName - Name of the process
262
+ * @param version - Version number to point the alias to
263
+ * @param alias - Alias name
264
+ * @returns Updated alias details
265
+ */
266
+ export async function updateAlias(
267
+ apiKey: string | undefined,
268
+ marketplace: string,
269
+ processName: string,
270
+ version: number,
271
+ alias: string
272
+ ): Promise<AliasResult> {
273
+ const response = await apiPostTransit<{
274
+ data: { 'processAlias/alias': string; 'processAlias/version': number };
275
+ }>(
276
+ apiKey,
277
+ '/aliases/update-alias',
278
+ { marketplace },
279
+ keywordMap({
280
+ name: keyword(processName),
281
+ version,
282
+ alias: keyword(alias),
283
+ })
284
+ );
285
+
286
+ return {
287
+ alias: response.data['processAlias/alias'],
288
+ version: response.data['processAlias/version'],
289
+ };
290
+ }
291
+
292
+ /**
293
+ * Deletes a process alias
294
+ *
295
+ * @param apiKey - Sharetribe API key
296
+ * @param marketplace - Marketplace ID
297
+ * @param processName - Name of the process
298
+ * @param alias - Alias name to delete
299
+ * @returns Deleted alias name
300
+ */
301
+ export async function deleteAlias(
302
+ apiKey: string | undefined,
303
+ marketplace: string,
304
+ processName: string,
305
+ alias: string
306
+ ): Promise<{ alias: string }> {
307
+ const response = await apiPostTransit<{ data: { 'processAlias/alias': string } }>(
308
+ apiKey,
309
+ '/aliases/delete-alias',
310
+ { marketplace },
311
+ keywordMap({
312
+ name: keyword(processName),
313
+ alias: keyword(alias),
314
+ })
315
+ );
316
+
317
+ return {
318
+ alias: response.data['processAlias/alias'],
319
+ };
320
+ }
@@ -0,0 +1,25 @@
1
+ # SDK Exports Documentation
2
+
3
+ This SDK provides programmatic access to all Sharetribe CLI commands.
4
+
5
+ ## Modules
6
+
7
+ - **processes.ts**: Process management (list, create, push, pull, aliases)
8
+ - **search.ts**: Search schema management (list, set, unset) - TODO
9
+ - **assets.ts**: Asset management (pull, push) - TODO
10
+ - **notifications.ts**: Email notifications (preview, send) - TODO
11
+ - **listing-approval.ts**: Listing approval settings - TODO
12
+ - **stripe.ts**: Stripe configuration - TODO
13
+ - **events.ts**: Event webhooks - TODO
14
+
15
+ ## Implementation Status
16
+
17
+ ✅ Processes - Complete
18
+ ⏳ Search - In progress
19
+ ⏳ Assets - In progress
20
+ ⏳ Notifications - In progress
21
+ ⏳ Listing approval - In progress
22
+ ⏳ Stripe - In progress
23
+ ⏳ Events - In progress
24
+
25
+ Note: For the first release (1.15.0), we're focusing on process management which is the core functionality. Additional commands will be added in future versions while maintaining backward compatibility.
package/src/search.ts ADDED
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Search schema management functions
3
+ *
4
+ * Programmatic API for managing search schemas
5
+ */
6
+
7
+ import { apiGet, apiPostTransit } from './api/client.js';
8
+ import { keyword, keywordMap } from './api/transit.js';
9
+
10
+ export interface SearchSchema {
11
+ schemaFor: string;
12
+ scope: string;
13
+ key: string;
14
+ type: string;
15
+ defaultValue?: string | number | boolean | string[];
16
+ doc?: string;
17
+ }
18
+
19
+ export interface SetSearchSchemaOptions {
20
+ key: string;
21
+ scope: string;
22
+ type: string;
23
+ doc?: string;
24
+ defaultValue?: string;
25
+ schemaFor?: string;
26
+ }
27
+
28
+ export interface UnsetSearchSchemaOptions {
29
+ key: string;
30
+ scope: string;
31
+ schemaFor?: string;
32
+ }
33
+
34
+ /**
35
+ * Valid values for schema-for parameter
36
+ */
37
+ const VALID_SCHEMA_FOR = ['listing', 'userProfile', 'transaction'];
38
+
39
+ /**
40
+ * Valid scopes by schema type
41
+ */
42
+ const VALID_SCOPES: Record<string, string[]> = {
43
+ listing: ['metadata', 'public'],
44
+ userProfile: ['metadata', 'private', 'protected', 'public'],
45
+ transaction: ['metadata', 'protected'],
46
+ };
47
+
48
+ /**
49
+ * Valid types
50
+ */
51
+ const VALID_TYPES = ['enum', 'multi-enum', 'boolean', 'long', 'text'];
52
+
53
+ /**
54
+ * Validates and coerces a default value based on type
55
+ */
56
+ function coerceDefaultValue(value: string, type: string): string | number | boolean | string[] {
57
+ if (type === 'boolean') {
58
+ if (value !== 'true' && value !== 'false') {
59
+ throw new Error(`Default value must be "true" or "false" for boolean type`);
60
+ }
61
+ return value === 'true';
62
+ }
63
+
64
+ if (type === 'long') {
65
+ const num = parseFloat(value);
66
+ if (isNaN(num) || !Number.isInteger(num)) {
67
+ throw new Error(`Default value must be an integer for long type`);
68
+ }
69
+ return num;
70
+ }
71
+
72
+ if (type === 'multi-enum') {
73
+ return value.split(',').map(v => v.trim());
74
+ }
75
+
76
+ return value;
77
+ }
78
+
79
+ /**
80
+ * Validates search schema parameters for set operation
81
+ */
82
+ function validateSetParams(opts: SetSearchSchemaOptions): void {
83
+ const errors: string[] = [];
84
+ const schemaFor = opts.schemaFor || 'listing';
85
+
86
+ if (opts.key.includes('.')) {
87
+ errors.push('Key cannot include dots (.) - only top-level keys are allowed');
88
+ }
89
+
90
+ if (!VALID_SCHEMA_FOR.includes(schemaFor)) {
91
+ errors.push(`schema-for must be one of: ${VALID_SCHEMA_FOR.join(', ')}`);
92
+ }
93
+
94
+ const validScopes = VALID_SCOPES[schemaFor] || [];
95
+ if (!validScopes.includes(opts.scope)) {
96
+ errors.push(`scope must be one of: ${validScopes.join(', ')} for ${schemaFor}`);
97
+ }
98
+
99
+ if (!VALID_TYPES.includes(opts.type)) {
100
+ errors.push(`type must be one of: ${VALID_TYPES.join(', ')}`);
101
+ }
102
+
103
+ if (opts.type === 'text' && schemaFor === 'userProfile') {
104
+ errors.push('text type is not supported for userProfile schema');
105
+ }
106
+
107
+ if (errors.length > 0) {
108
+ throw new Error(errors.join('\n'));
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Validates search schema parameters for unset operation
114
+ */
115
+ function validateUnsetParams(opts: UnsetSearchSchemaOptions): void {
116
+ const errors: string[] = [];
117
+ const schemaFor = opts.schemaFor || 'listing';
118
+
119
+ if (opts.key.includes('.')) {
120
+ errors.push('Key cannot include dots (.) - only top-level keys are allowed');
121
+ }
122
+
123
+ if (!VALID_SCHEMA_FOR.includes(schemaFor)) {
124
+ errors.push(`schema-for must be one of: ${VALID_SCHEMA_FOR.join(', ')}`);
125
+ }
126
+
127
+ const validScopes = VALID_SCOPES[schemaFor] || [];
128
+ if (!validScopes.includes(opts.scope)) {
129
+ errors.push(`scope must be one of: ${validScopes.join(', ')} for ${schemaFor}`);
130
+ }
131
+
132
+ if (errors.length > 0) {
133
+ throw new Error(errors.join('\n'));
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Extracts the name from a namespaced key (e.g., "dataSchema.scope/public" -> "public")
139
+ */
140
+ function extractName(key: string): string {
141
+ return key.split('/').pop() || key;
142
+ }
143
+
144
+ /**
145
+ * Converts type and cardinality to type label (e.g., "multi-enum" for enum with many cardinality)
146
+ */
147
+ function getTypeLabel(valueType: string, cardinality?: string): string {
148
+ const typeName = valueType.split('/').pop() || valueType;
149
+ if (typeName === 'enum' && cardinality === 'dataSchema.cardinality/many') {
150
+ return 'multi-enum';
151
+ }
152
+ return typeName;
153
+ }
154
+
155
+ /**
156
+ * Converts default value to display format
157
+ */
158
+ function formatDefaultValue(value: unknown): string | number | boolean | string[] | undefined {
159
+ if (value === undefined || value === null) {
160
+ return undefined;
161
+ }
162
+ if (Array.isArray(value)) {
163
+ return value;
164
+ }
165
+ return value as string | number | boolean;
166
+ }
167
+
168
+ /**
169
+ * Lists all search schemas for a marketplace
170
+ *
171
+ * @param apiKey - Sharetribe API key (optional, reads from auth file if not provided)
172
+ * @param marketplace - Marketplace ID
173
+ * @returns Array of search schemas
174
+ */
175
+ export async function listSearchSchemas(
176
+ apiKey: string | undefined,
177
+ marketplace: string
178
+ ): Promise<SearchSchema[]> {
179
+ const response = await apiGet<{
180
+ data: Array<{
181
+ 'dataSchema/key': string;
182
+ 'dataSchema/scope': string;
183
+ 'dataSchema/valueType': string;
184
+ 'dataSchema/cardinality'?: string;
185
+ 'dataSchema/defaultValue'?: unknown;
186
+ 'dataSchema/doc'?: string;
187
+ 'dataSchema/of': string;
188
+ }>;
189
+ }>(
190
+ apiKey,
191
+ '/search-schemas/query',
192
+ {
193
+ marketplace,
194
+ of: 'dataSchema.of/userProfile,dataSchema.of/listing,dataSchema.of/transaction',
195
+ }
196
+ );
197
+
198
+ return response.data.map(s => ({
199
+ schemaFor: extractName(s['dataSchema/of']),
200
+ scope: extractName(s['dataSchema/scope']),
201
+ key: extractName(s['dataSchema/key']),
202
+ type: getTypeLabel(s['dataSchema/valueType'], s['dataSchema/cardinality']),
203
+ defaultValue: formatDefaultValue(s['dataSchema/defaultValue']),
204
+ doc: s['dataSchema/doc'],
205
+ }));
206
+ }
207
+
208
+ /**
209
+ * Sets a search schema field
210
+ *
211
+ * @param apiKey - Sharetribe API key (optional, reads from auth file if not provided)
212
+ * @param marketplace - Marketplace ID
213
+ * @param options - Schema configuration
214
+ * @returns Success confirmation
215
+ */
216
+ export async function setSearchSchema(
217
+ apiKey: string | undefined,
218
+ marketplace: string,
219
+ options: SetSearchSchemaOptions
220
+ ): Promise<{ success: true }> {
221
+ validateSetParams(options);
222
+
223
+ const schemaFor = options.schemaFor || 'listing';
224
+ const isMultiEnum = options.type === 'multi-enum';
225
+
226
+ const bodyObj: Record<string, unknown> = {
227
+ key: keyword(options.key),
228
+ scope: keyword(`dataSchema.scope/${options.scope}`),
229
+ valueType: keyword(`dataSchema.type/${isMultiEnum ? 'enum' : options.type}`),
230
+ cardinality: keyword(isMultiEnum ? 'dataSchema.cardinality/many' : 'dataSchema.cardinality/one'),
231
+ of: keyword(`dataSchema.of/${schemaFor}`),
232
+ };
233
+
234
+ if (options.doc) {
235
+ bodyObj.doc = options.doc;
236
+ }
237
+
238
+ if (options.defaultValue !== undefined) {
239
+ bodyObj.defaultValue = coerceDefaultValue(options.defaultValue, options.type);
240
+ }
241
+
242
+ await apiPostTransit(apiKey, '/search-schemas/set', { marketplace }, keywordMap(bodyObj));
243
+
244
+ return { success: true };
245
+ }
246
+
247
+ /**
248
+ * Unsets a search schema field
249
+ *
250
+ * @param apiKey - Sharetribe API key (optional, reads from auth file if not provided)
251
+ * @param marketplace - Marketplace ID
252
+ * @param options - Schema identification
253
+ * @returns Success confirmation
254
+ */
255
+ export async function unsetSearchSchema(
256
+ apiKey: string | undefined,
257
+ marketplace: string,
258
+ options: UnsetSearchSchemaOptions
259
+ ): Promise<{ success: true }> {
260
+ validateUnsetParams(options);
261
+
262
+ const schemaFor = options.schemaFor || 'listing';
263
+
264
+ const bodyObj = {
265
+ key: keyword(options.key),
266
+ scope: keyword(`dataSchema.scope/${options.scope}`),
267
+ of: keyword(`dataSchema.of/${schemaFor}`),
268
+ };
269
+
270
+ await apiPostTransit(apiKey, '/search-schemas/unset', { marketplace }, keywordMap(bodyObj));
271
+
272
+ return { success: true };
273
+ }
package/src/stripe.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Stripe integration management functions
3
+ *
4
+ * Programmatic API for managing Stripe integration
5
+ */
6
+
7
+ import { apiPost } from './api/client.js';
8
+
9
+ /**
10
+ * Supported Stripe API versions
11
+ */
12
+ export const SUPPORTED_STRIPE_VERSIONS = ['2019-12-03', '2019-09-09', '2019-02-19'] as const;
13
+
14
+ export type StripeApiVersion = typeof SUPPORTED_STRIPE_VERSIONS[number];
15
+
16
+ /**
17
+ * Updates Stripe API version
18
+ *
19
+ * @param apiKey - Sharetribe API key (optional, reads from auth file if not provided)
20
+ * @param marketplace - Marketplace ID
21
+ * @param version - Stripe API version
22
+ * @returns Success confirmation
23
+ */
24
+ export async function updateStripeVersion(
25
+ apiKey: string | undefined,
26
+ marketplace: string,
27
+ version: string
28
+ ): Promise<{ success: true }> {
29
+ // Validate version
30
+ if (!SUPPORTED_STRIPE_VERSIONS.includes(version as StripeApiVersion)) {
31
+ throw new Error(
32
+ `--version should be one of: ${SUPPORTED_STRIPE_VERSIONS.join(', ')}. Was ${version}.`
33
+ );
34
+ }
35
+
36
+ await apiPost(apiKey, '/stripe/update-version', { marketplace }, { version });
37
+
38
+ return { success: true };
39
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Type definitions for jsedn
3
+ */
4
+
5
+ declare module 'jsedn' {
6
+ export function parse(str: string): any;
7
+ export function kw(name: string): any;
8
+ export function encode(value: any): string;
9
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Type definitions for transit-js
3
+ */
4
+
5
+ declare module 'transit-js' {
6
+ export function writer(type: string, options?: { handlers?: any }): any;
7
+ export function reader(type: string): any;
8
+ export function keyword(name: string): any;
9
+ export function map(entries: any[]): any;
10
+ }