@awesomate/sdk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +119 -4
- package/dist/index.js +59 -14
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -6,16 +6,37 @@
|
|
|
6
6
|
* const db = createClient({ token: process.env.AWESOMATE_TOKEN! });
|
|
7
7
|
* const { rows, next } = await db.query('contact', { where: { created_at: { gte: '$YEAR_BEGIN' } }, limit: 50 });
|
|
8
8
|
*
|
|
9
|
+
* const open = await db.run('open_jobs_in', { suburb: 'Carindale' }); // a saved query, by name
|
|
10
|
+
* await db.call('log_visit', { customer, title: 'Possum in the roof' }); // a write recipe, one transaction
|
|
11
|
+
*
|
|
9
12
|
* The account's token (a hosting PAT with crm:read) is a secret: use it on a server, never in a
|
|
10
|
-
* browser. End-user sign-in for browser apps comes in a later version.
|
|
11
|
-
*
|
|
13
|
+
* browser. End-user sign-in for browser apps comes in a later version. Reading and running saved
|
|
14
|
+
* queries works on every plan; writing app data (kinds, records, saved queries, recipes) needs
|
|
15
|
+
* crm:write, Support Plus and above.
|
|
12
16
|
*/
|
|
13
|
-
export declare const VERSION = "0.
|
|
17
|
+
export declare const VERSION = "0.3.0";
|
|
14
18
|
/** Augmented by the generated awesomate.d.ts, so each kind's rows are typed. */
|
|
15
19
|
export interface Kinds {
|
|
16
20
|
}
|
|
17
21
|
export type KindName = [keyof Kinds] extends [never] ? string : Extract<keyof Kinds, string>;
|
|
18
22
|
export type RowOf<K> = K extends keyof Kinds ? Kinds[K] : Record<string, unknown>;
|
|
23
|
+
/** Augmented by the generated awesomate.d.ts: each saved query's params and row. */
|
|
24
|
+
export interface Queries {
|
|
25
|
+
}
|
|
26
|
+
/** Augmented by the generated awesomate.d.ts: each write recipe's args. */
|
|
27
|
+
export interface Recipes {
|
|
28
|
+
}
|
|
29
|
+
export type QueryName = [keyof Queries] extends [never] ? string : Extract<keyof Queries, string>;
|
|
30
|
+
export type ParamsOf<Q> = Q extends keyof Queries ? (Queries[Q] extends {
|
|
31
|
+
params: infer P;
|
|
32
|
+
} ? P : never) : Record<string, unknown>;
|
|
33
|
+
export type QueryRowOf<Q> = Q extends keyof Queries ? (Queries[Q] extends {
|
|
34
|
+
row: infer R;
|
|
35
|
+
} ? R : never) : Record<string, unknown>;
|
|
36
|
+
export type RecipeName = [keyof Recipes] extends [never] ? string : Extract<keyof Recipes, string>;
|
|
37
|
+
export type ArgsOf<R> = R extends keyof Recipes ? (Recipes[R] extends {
|
|
38
|
+
args: infer A;
|
|
39
|
+
} ? A : never) : Record<string, unknown>;
|
|
19
40
|
type TextOps<V extends string> = {
|
|
20
41
|
eq?: V | null;
|
|
21
42
|
neq?: V | null;
|
|
@@ -123,7 +144,7 @@ export declare class AwesomateClient {
|
|
|
123
144
|
private readonly base;
|
|
124
145
|
private readonly doFetch;
|
|
125
146
|
constructor(opts: ClientOptions);
|
|
126
|
-
private
|
|
147
|
+
private request;
|
|
127
148
|
/** The kinds this token can read, their columns, operators and examples. */
|
|
128
149
|
schema(): Promise<{
|
|
129
150
|
kinds: Array<{
|
|
@@ -160,6 +181,34 @@ export declare class AwesomateClient {
|
|
|
160
181
|
write<K extends KindName>(kind: K, data: Partial<RowOf<K>> | Record<string, unknown>, options?: WriteOptions): Promise<string>;
|
|
161
182
|
/** Archive a record: it leaves every read and is kept for a restore. */
|
|
162
183
|
archive<K extends KindName>(kind: K, id: string): Promise<void>;
|
|
184
|
+
/** The saved queries on this account. */
|
|
185
|
+
queries(): Promise<SavedQueryDescription[]>;
|
|
186
|
+
/**
|
|
187
|
+
* Save a query by name (saving a name again replaces it). The spec is the query() grammar on one
|
|
188
|
+
* kind, with {"$param": "<name>"} where a caller's value goes; it is compiled before it is
|
|
189
|
+
* stored, so a column it cannot read is refused now rather than when it runs.
|
|
190
|
+
*/
|
|
191
|
+
saveQuery(spec: SavedQuerySpec): Promise<SavedQueryDescription>;
|
|
192
|
+
archiveQuery(key: string): Promise<void>;
|
|
193
|
+
/** Run a saved query with its params. An optional param left out drops its condition. */
|
|
194
|
+
run<Q extends QueryName>(query: Q, params?: ParamsOf<Q>, options?: {
|
|
195
|
+
limit?: number;
|
|
196
|
+
after?: string;
|
|
197
|
+
tz?: string;
|
|
198
|
+
}): Promise<Page<QueryRowOf<Q>>>;
|
|
199
|
+
/** The write recipes on this account. */
|
|
200
|
+
recipes(): Promise<RecipeDescription[]>;
|
|
201
|
+
/**
|
|
202
|
+
* Save a write recipe: up to 10 write_record / archive_record steps run in one transaction.
|
|
203
|
+
* {"$param": "<name>"} takes a caller's value, {"$step": "<as>"} the id an earlier step wrote.
|
|
204
|
+
*/
|
|
205
|
+
saveRecipe(spec: RecipeSpec): Promise<RecipeDescription>;
|
|
206
|
+
archiveRecipe(key: string): Promise<void>;
|
|
207
|
+
/**
|
|
208
|
+
* Run a write recipe. Every step commits or none does; a refusal names the step and the field.
|
|
209
|
+
* Never retried: a write that may have landed is not safe to send twice.
|
|
210
|
+
*/
|
|
211
|
+
call<R extends RecipeName>(recipe: R, args?: ArgsOf<R>): Promise<RecipeResult>;
|
|
163
212
|
/** Grouped numbers (counts, sums) in the Business Data API's shape. */
|
|
164
213
|
aggregate(request: {
|
|
165
214
|
measures: Array<{
|
|
@@ -227,5 +276,71 @@ export interface WriteOptions {
|
|
|
227
276
|
/** Set a link by its key ('<id>'), or end it (null). */
|
|
228
277
|
links?: Record<string, string | null>;
|
|
229
278
|
}
|
|
279
|
+
export type ParamType = 'text' | 'number' | 'date' | 'datetime' | 'boolean' | 'uuid' | 'text_list' | 'number_list';
|
|
280
|
+
export interface ParamSpec {
|
|
281
|
+
name: string;
|
|
282
|
+
/** Default text. */
|
|
283
|
+
type?: ParamType;
|
|
284
|
+
required?: boolean;
|
|
285
|
+
default?: unknown;
|
|
286
|
+
label?: string;
|
|
287
|
+
}
|
|
288
|
+
/** Where a caller's value goes in a saved query or a recipe. */
|
|
289
|
+
export type Param = {
|
|
290
|
+
$param: string;
|
|
291
|
+
};
|
|
292
|
+
export interface SavedQuerySpec {
|
|
293
|
+
key: string;
|
|
294
|
+
label: string;
|
|
295
|
+
description?: string;
|
|
296
|
+
/** 'contact' or an app kind. */
|
|
297
|
+
kind: string;
|
|
298
|
+
spec: {
|
|
299
|
+
where?: Record<string, unknown>;
|
|
300
|
+
orderBy?: Array<[string, 'asc' | 'desc']>;
|
|
301
|
+
select?: string[];
|
|
302
|
+
limit?: number;
|
|
303
|
+
};
|
|
304
|
+
params?: ParamSpec[];
|
|
305
|
+
}
|
|
306
|
+
export interface SavedQueryDescription extends Required<Omit<SavedQuerySpec, 'description' | 'params'>> {
|
|
307
|
+
description: string | null;
|
|
308
|
+
params: ParamSpec[];
|
|
309
|
+
updated_at: string;
|
|
310
|
+
}
|
|
311
|
+
export type RecipeStep = {
|
|
312
|
+
op: 'write_record';
|
|
313
|
+
kind: string;
|
|
314
|
+
id?: Param | {
|
|
315
|
+
$step: string;
|
|
316
|
+
};
|
|
317
|
+
as?: string;
|
|
318
|
+
data?: Record<string, unknown>;
|
|
319
|
+
links?: Record<string, unknown>;
|
|
320
|
+
} | {
|
|
321
|
+
op: 'archive_record';
|
|
322
|
+
kind: string;
|
|
323
|
+
id: Param | {
|
|
324
|
+
$step: string;
|
|
325
|
+
};
|
|
326
|
+
};
|
|
327
|
+
export interface RecipeSpec {
|
|
328
|
+
key: string;
|
|
329
|
+
label: string;
|
|
330
|
+
description?: string;
|
|
331
|
+
params?: ParamSpec[];
|
|
332
|
+
steps: RecipeStep[];
|
|
333
|
+
}
|
|
334
|
+
export interface RecipeDescription extends Required<Omit<RecipeSpec, 'description' | 'params'>> {
|
|
335
|
+
description: string | null;
|
|
336
|
+
params: ParamSpec[];
|
|
337
|
+
updated_at: string;
|
|
338
|
+
}
|
|
339
|
+
export interface RecipeResult {
|
|
340
|
+
/** The id each step named with `as` wrote. */
|
|
341
|
+
ids: Record<string, string>;
|
|
342
|
+
/** Every record the recipe wrote or archived, in step order. */
|
|
343
|
+
records: string[];
|
|
344
|
+
}
|
|
230
345
|
export declare function createClient(options: ClientOptions): AwesomateClient;
|
|
231
346
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -6,11 +6,15 @@
|
|
|
6
6
|
* const db = createClient({ token: process.env.AWESOMATE_TOKEN! });
|
|
7
7
|
* const { rows, next } = await db.query('contact', { where: { created_at: { gte: '$YEAR_BEGIN' } }, limit: 50 });
|
|
8
8
|
*
|
|
9
|
+
* const open = await db.run('open_jobs_in', { suburb: 'Carindale' }); // a saved query, by name
|
|
10
|
+
* await db.call('log_visit', { customer, title: 'Possum in the roof' }); // a write recipe, one transaction
|
|
11
|
+
*
|
|
9
12
|
* The account's token (a hosting PAT with crm:read) is a secret: use it on a server, never in a
|
|
10
|
-
* browser. End-user sign-in for browser apps comes in a later version.
|
|
11
|
-
*
|
|
13
|
+
* browser. End-user sign-in for browser apps comes in a later version. Reading and running saved
|
|
14
|
+
* queries works on every plan; writing app data (kinds, records, saved queries, recipes) needs
|
|
15
|
+
* crm:write, Support Plus and above.
|
|
12
16
|
*/
|
|
13
|
-
export const VERSION = '0.
|
|
17
|
+
export const VERSION = '0.3.0';
|
|
14
18
|
const DEFAULT_BASE = 'https://hub.awesomate.ai';
|
|
15
19
|
export class AwesomateError extends Error {
|
|
16
20
|
code;
|
|
@@ -58,7 +62,7 @@ export class AwesomateClient {
|
|
|
58
62
|
if (!this.doFetch)
|
|
59
63
|
throw new Error('No fetch available: use Node 18 or later, or pass fetch.');
|
|
60
64
|
}
|
|
61
|
-
async
|
|
65
|
+
async request(method, path, body, retry = true) {
|
|
62
66
|
const res = await this.doFetch(`${this.base}${path}`, {
|
|
63
67
|
method,
|
|
64
68
|
headers: {
|
|
@@ -81,20 +85,20 @@ export class AwesomateClient {
|
|
|
81
85
|
// The field list changed while the read ran (the view is rebuilt on every field edit): a read
|
|
82
86
|
// is safe to run again, once.
|
|
83
87
|
if (code === 'conflict' && retry)
|
|
84
|
-
return this.
|
|
88
|
+
return this.request(method, path, body, false);
|
|
85
89
|
const message = typeof json.error === 'string' ? json.error : `The hub answered ${res.status}.`;
|
|
86
90
|
throw new AwesomateError(code, message, res.status, typeof json.field === 'string' ? json.field : undefined, server);
|
|
87
91
|
}
|
|
88
92
|
/** The kinds this token can read, their columns, operators and examples. */
|
|
89
93
|
schema() {
|
|
90
|
-
return this.
|
|
94
|
+
return this.request('GET', '/api/my-crm/v1/rows/schema');
|
|
91
95
|
}
|
|
92
96
|
/** The readable kinds as a TypeScript file (what the types command writes). */
|
|
93
97
|
types() {
|
|
94
|
-
return this.
|
|
98
|
+
return this.request('GET', '/api/my-crm/v1/rows/types');
|
|
95
99
|
}
|
|
96
100
|
async query(kind, options = {}) {
|
|
97
|
-
const r = await this.
|
|
101
|
+
const r = await this.request('POST', '/api/my-crm/v1/rows/query', { kind, ...options });
|
|
98
102
|
return { rows: r.rows, next: r.next, count: r.count, applied: r.applied, hidden: r.hidden, asAt: r.as_at };
|
|
99
103
|
}
|
|
100
104
|
/** Every matching row, a page at a time. maxRows guards against reading a whole list by accident. */
|
|
@@ -116,7 +120,7 @@ export class AwesomateClient {
|
|
|
116
120
|
async get(kind, id, options = {}) {
|
|
117
121
|
try {
|
|
118
122
|
const qs = options.tz ? `?tz=${encodeURIComponent(options.tz)}` : '';
|
|
119
|
-
const r = await this.
|
|
123
|
+
const r = await this.request('GET', `/api/my-crm/v1/rows/${encodeURIComponent(kind)}/${encodeURIComponent(id)}${qs}`);
|
|
120
124
|
return r.row;
|
|
121
125
|
}
|
|
122
126
|
catch (err) {
|
|
@@ -127,26 +131,67 @@ export class AwesomateClient {
|
|
|
127
131
|
}
|
|
128
132
|
/** The app kinds this account has defined. */
|
|
129
133
|
async kinds() {
|
|
130
|
-
return (await this.
|
|
134
|
+
return (await this.request('GET', '/api/my-crm/v1/kinds')).kinds;
|
|
131
135
|
}
|
|
132
136
|
/** Define an app kind (a table an app keeps). No migration: registry rows and generated views. */
|
|
133
137
|
async defineKind(spec) {
|
|
134
|
-
return (await this.
|
|
138
|
+
return (await this.request('POST', '/api/my-crm/v1/kinds', spec)).kind;
|
|
135
139
|
}
|
|
136
140
|
/** Create a record (returns its id), or change one with options.id. Every value is checked against the kind. */
|
|
137
141
|
async write(kind, data, options = {}) {
|
|
138
|
-
const r = await this.
|
|
142
|
+
const r = await this.request('POST', '/api/my-crm/v1/call', {
|
|
139
143
|
fn: 'write_record', args: { kind, data, links: options.links ?? {}, ...(options.id ? { id: options.id } : {}) },
|
|
140
144
|
}, false);
|
|
141
145
|
return r.result.id;
|
|
142
146
|
}
|
|
143
147
|
/** Archive a record: it leaves every read and is kept for a restore. */
|
|
144
148
|
async archive(kind, id) {
|
|
145
|
-
await this.
|
|
149
|
+
await this.request('POST', '/api/my-crm/v1/call', { fn: 'archive_record', args: { kind, id } }, false);
|
|
150
|
+
}
|
|
151
|
+
/** The saved queries on this account. */
|
|
152
|
+
async queries() {
|
|
153
|
+
return (await this.request('GET', '/api/my-crm/v1/queries')).queries;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Save a query by name (saving a name again replaces it). The spec is the query() grammar on one
|
|
157
|
+
* kind, with {"$param": "<name>"} where a caller's value goes; it is compiled before it is
|
|
158
|
+
* stored, so a column it cannot read is refused now rather than when it runs.
|
|
159
|
+
*/
|
|
160
|
+
async saveQuery(spec) {
|
|
161
|
+
return (await this.request('POST', '/api/my-crm/v1/queries', spec, false)).query;
|
|
162
|
+
}
|
|
163
|
+
async archiveQuery(key) {
|
|
164
|
+
await this.request('DELETE', `/api/my-crm/v1/queries/${encodeURIComponent(key)}`, undefined, false);
|
|
165
|
+
}
|
|
166
|
+
/** Run a saved query with its params. An optional param left out drops its condition. */
|
|
167
|
+
async run(query, params = {}, options = {}) {
|
|
168
|
+
const r = await this.request('POST', `/api/my-crm/v1/queries/${encodeURIComponent(query)}/run`, { params, ...options });
|
|
169
|
+
return { rows: r.rows, next: r.next, count: r.count, applied: r.applied, hidden: r.hidden, asAt: r.as_at };
|
|
170
|
+
}
|
|
171
|
+
/** The write recipes on this account. */
|
|
172
|
+
async recipes() {
|
|
173
|
+
return (await this.request('GET', '/api/my-crm/v1/recipes')).recipes;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Save a write recipe: up to 10 write_record / archive_record steps run in one transaction.
|
|
177
|
+
* {"$param": "<name>"} takes a caller's value, {"$step": "<as>"} the id an earlier step wrote.
|
|
178
|
+
*/
|
|
179
|
+
async saveRecipe(spec) {
|
|
180
|
+
return (await this.request('POST', '/api/my-crm/v1/recipes', spec, false)).recipe;
|
|
181
|
+
}
|
|
182
|
+
async archiveRecipe(key) {
|
|
183
|
+
await this.request('DELETE', `/api/my-crm/v1/recipes/${encodeURIComponent(key)}`, undefined, false);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Run a write recipe. Every step commits or none does; a refusal names the step and the field.
|
|
187
|
+
* Never retried: a write that may have landed is not safe to send twice.
|
|
188
|
+
*/
|
|
189
|
+
async call(recipe, args = {}) {
|
|
190
|
+
return (await this.request('POST', '/api/my-crm/v1/call', { fn: recipe, args }, false)).result;
|
|
146
191
|
}
|
|
147
192
|
/** Grouped numbers (counts, sums) in the Business Data API's shape. */
|
|
148
193
|
aggregate(request) {
|
|
149
|
-
return this.
|
|
194
|
+
return this.request('POST', '/api/my-crm/v1/data/query', request);
|
|
150
195
|
}
|
|
151
196
|
}
|
|
152
197
|
export function createClient(options) {
|
package/package.json
CHANGED