@doany-ai/sdk 0.2.9-alpha.0 → 0.3.0-alpha.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/client.js +51 -7
- package/dist/client.types.d.ts +55 -5
- package/dist/index.d.ts +8 -4
- package/dist/modules/auth.js +6 -0
- package/dist/modules/auth.types.d.ts +30 -0
- package/dist/modules/catalog.d.ts +3 -2
- package/dist/modules/catalog.js +53 -20
- package/dist/modules/catalog.types.d.ts +118 -18
- package/dist/modules/contacts.d.ts +9 -0
- package/dist/modules/contacts.js +42 -0
- package/dist/modules/contacts.types.d.ts +112 -0
- package/dist/modules/contacts.types.js +1 -0
- package/dist/modules/events.d.ts +9 -0
- package/dist/modules/events.js +15 -0
- package/dist/modules/events.types.d.ts +42 -0
- package/dist/modules/events.types.js +1 -0
- package/dist/modules/order-access.d.ts +20 -0
- package/dist/modules/order-access.js +84 -0
- package/dist/modules/orders.d.ts +6 -2
- package/dist/modules/orders.js +125 -159
- package/dist/modules/orders.types.d.ts +121 -31
- package/dist/modules/payments.d.ts +2 -1
- package/dist/modules/payments.js +28 -1
- package/dist/modules/payments.types.d.ts +146 -0
- package/dist/modules/project.d.ts +31 -0
- package/dist/modules/project.js +52 -0
- package/dist/modules/project.types.d.ts +58 -0
- package/dist/modules/project.types.js +1 -0
- package/dist/modules/users.d.ts +7 -13
- package/dist/modules/users.js +24 -11
- package/dist/modules/users.types.d.ts +62 -0
- package/dist/modules/users.types.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { Address, CreateOptions, Page, PageParams, VersionInput } from "./project.types";
|
|
2
|
+
export type ContactSource = "website_registration" | "purchase" | "booking_form" | "contact_form" | "staff_entry" | "agent" | "import" | "api" | "unknown";
|
|
3
|
+
/**
|
|
4
|
+
* Someone the business knows: a customer, a lead, someone who wrote in.
|
|
5
|
+
*
|
|
6
|
+
* `business_note`, `source`, `app_id`, `merged_into_id` and `merged_at` are
|
|
7
|
+
* shown to the site's admin and service callers only.
|
|
8
|
+
*/
|
|
9
|
+
export interface Contact {
|
|
10
|
+
id: string;
|
|
11
|
+
/** Always set. */
|
|
12
|
+
display_name: string;
|
|
13
|
+
given_name: string | null;
|
|
14
|
+
family_name: string | null;
|
|
15
|
+
/** Lower-case. */
|
|
16
|
+
email: string | null;
|
|
17
|
+
/** E.164, e.g. `+14155550123`. */
|
|
18
|
+
phone: string | null;
|
|
19
|
+
address: Address | null;
|
|
20
|
+
timezone: string | null;
|
|
21
|
+
preferred_language: string | null;
|
|
22
|
+
business_note?: string | null;
|
|
23
|
+
source?: ContactSource;
|
|
24
|
+
/** The site it came from; `null` for one the business entered. */
|
|
25
|
+
app_id?: string | null;
|
|
26
|
+
archived_at: string | null;
|
|
27
|
+
merged_into_id?: string | null;
|
|
28
|
+
merged_at?: string | null;
|
|
29
|
+
version: number;
|
|
30
|
+
created_at: string;
|
|
31
|
+
updated_at: string;
|
|
32
|
+
}
|
|
33
|
+
/** An account linked to a contact. */
|
|
34
|
+
export interface ContactUserLink {
|
|
35
|
+
user_id: string;
|
|
36
|
+
email: string | null;
|
|
37
|
+
/** `self`: the account is this person. */
|
|
38
|
+
role: "self" | "admin" | "billing" | "member";
|
|
39
|
+
/** `records`: sees the contact's orders and buys as it. `profile`: its details only. */
|
|
40
|
+
access: "profile" | "records";
|
|
41
|
+
/** The contact this account buys as by default. */
|
|
42
|
+
is_default: boolean;
|
|
43
|
+
granted_via: "registration" | "email_match" | "staff" | "checkout" | "invite" | "claim";
|
|
44
|
+
status: "active" | "revoked";
|
|
45
|
+
}
|
|
46
|
+
/** A contact with the accounts linked to it (the latter for admin and service callers). */
|
|
47
|
+
export interface ContactDetail extends Contact {
|
|
48
|
+
users?: ContactUserLink[];
|
|
49
|
+
}
|
|
50
|
+
export interface ContactListParams extends PageParams {
|
|
51
|
+
/** Part of the name, email or phone. */
|
|
52
|
+
q?: string;
|
|
53
|
+
/** `active` (default), `archived` or `all`. */
|
|
54
|
+
status?: "active" | "archived" | "all";
|
|
55
|
+
source?: ContactSource;
|
|
56
|
+
/** Contacts that came from this site. */
|
|
57
|
+
app_id?: string;
|
|
58
|
+
created_from?: string;
|
|
59
|
+
created_to?: string;
|
|
60
|
+
}
|
|
61
|
+
interface ContactFields {
|
|
62
|
+
given_name?: string | null;
|
|
63
|
+
family_name?: string | null;
|
|
64
|
+
email?: string | null;
|
|
65
|
+
phone?: string | null;
|
|
66
|
+
address?: Partial<Address> | null;
|
|
67
|
+
timezone?: string | null;
|
|
68
|
+
preferred_language?: string | null;
|
|
69
|
+
business_note?: string | null;
|
|
70
|
+
}
|
|
71
|
+
export interface CreateContactParams extends ContactFields {
|
|
72
|
+
/** Left out: the name, else the email, else the phone. */
|
|
73
|
+
display_name?: string | null;
|
|
74
|
+
/** `staff_entry` (default), `agent`, `import`, `api`. */
|
|
75
|
+
source?: "staff_entry" | "agent" | "import" | "api";
|
|
76
|
+
}
|
|
77
|
+
export interface UpdateContactParams extends ContactFields, VersionInput {
|
|
78
|
+
display_name?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The people the business knows.
|
|
82
|
+
*
|
|
83
|
+
* A signed-in account reads and changes only its own contact (`me`, and
|
|
84
|
+
* `updateMe` — not its email, which follows its sign-in). Everything else
|
|
85
|
+
* takes the site's admin account or a service credential.
|
|
86
|
+
*/
|
|
87
|
+
export interface ContactsModule {
|
|
88
|
+
/** Contacts the caller may see; an account sees only its own. Rejects with 401 when nobody is signed in. */
|
|
89
|
+
list(params?: ContactListParams): Promise<Page<Contact>>;
|
|
90
|
+
/** One contact. Rejects with 404 when there is none, or the caller may not see it. */
|
|
91
|
+
get(contactId: string): Promise<ContactDetail>;
|
|
92
|
+
/** The signed-in account's own contact. Rejects with 404 when it has none yet. */
|
|
93
|
+
me(): Promise<ContactDetail>;
|
|
94
|
+
/** A new contact. Rejects with 400 on a malformed email or phone. */
|
|
95
|
+
create(params: CreateContactParams, options?: CreateOptions): Promise<Contact>;
|
|
96
|
+
update(contactId: string, params: UpdateContactParams): Promise<Contact>;
|
|
97
|
+
/** Changes the signed-in account's own contact. */
|
|
98
|
+
updateMe(params: UpdateContactParams): Promise<Contact>;
|
|
99
|
+
archive(contactId: string, params: VersionInput): Promise<Contact>;
|
|
100
|
+
restore(contactId: string, params: VersionInput): Promise<Contact>;
|
|
101
|
+
/**
|
|
102
|
+
* Merges a duplicate into another contact, for good: its account links move
|
|
103
|
+
* to the target and its orders are found under the target from then on.
|
|
104
|
+
* Returns the target.
|
|
105
|
+
*/
|
|
106
|
+
merge(contactId: string, params: VersionInput & {
|
|
107
|
+
into_contact_id: string;
|
|
108
|
+
}): Promise<ContactDetail>;
|
|
109
|
+
/** Deletes a contact nothing refers to; otherwise 409 `IN_USE` (archive it instead). */
|
|
110
|
+
delete(contactId: string): Promise<void>;
|
|
111
|
+
}
|
|
112
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AxiosInstance } from "axios";
|
|
2
|
+
import { EventsModule } from "./events.types";
|
|
3
|
+
import { ProjectScope } from "./project.js";
|
|
4
|
+
/**
|
|
5
|
+
* Creates the events module: `/projects/{project_id}/events`.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare function createEventsModule(axios: AxiosInstance, project: ProjectScope): EventsModule;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { queryOf } from "./project.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates the events module: `/projects/{project_id}/events`.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export function createEventsModule(axios, project) {
|
|
8
|
+
return {
|
|
9
|
+
async list(params = {}) {
|
|
10
|
+
return (await axios.get(await project.path("/events"), {
|
|
11
|
+
params: queryOf(params),
|
|
12
|
+
}));
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Page } from "./project.types";
|
|
2
|
+
export type EventSubjectType = "order" | "payment" | "refund" | "dispute" | "contact";
|
|
3
|
+
/**
|
|
4
|
+
* Something that happened to an order, a payment, a refund, a dispute or a
|
|
5
|
+
* contact — `order.placed`, `payment.succeeded`, `contact.merged`, … — and who
|
|
6
|
+
* did it.
|
|
7
|
+
*/
|
|
8
|
+
export interface ProjectEvent {
|
|
9
|
+
id: string;
|
|
10
|
+
event_type: string;
|
|
11
|
+
subject_type: EventSubjectType;
|
|
12
|
+
subject_id: string;
|
|
13
|
+
/** `type`: `system`, `provider`, `founder` (+ `user_id`), `agent`, `customer` (+ `project_user_id`), `guest`. */
|
|
14
|
+
actor: {
|
|
15
|
+
type: string;
|
|
16
|
+
user_id?: string;
|
|
17
|
+
project_user_id?: string;
|
|
18
|
+
};
|
|
19
|
+
data: Record<string, any>;
|
|
20
|
+
occurred_at: string;
|
|
21
|
+
}
|
|
22
|
+
export interface EventListParams {
|
|
23
|
+
/** The order's events, and those of its payments, refunds and disputes. */
|
|
24
|
+
order_id?: string;
|
|
25
|
+
/** The contact's (and merged contacts') events, and those of their orders. */
|
|
26
|
+
contact_id?: string;
|
|
27
|
+
subject_type?: EventSubjectType;
|
|
28
|
+
subject_id?: string;
|
|
29
|
+
/** A full type (`order.placed`) or a prefix ending in `.` (`refund.`). */
|
|
30
|
+
event_type?: string;
|
|
31
|
+
occurred_from?: string;
|
|
32
|
+
occurred_to?: string;
|
|
33
|
+
limit?: number;
|
|
34
|
+
cursor?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The business's timeline. The site's admin and service callers only.
|
|
38
|
+
*/
|
|
39
|
+
export interface EventsModule {
|
|
40
|
+
/** Newest first. */
|
|
41
|
+
list(params?: EventListParams): Promise<Page<ProjectEvent>>;
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AxiosInstance } from "axios";
|
|
2
|
+
/**
|
|
3
|
+
* An order's access token (`X-Doany-Access-Token`): what lets a guest come back
|
|
4
|
+
* to an order they placed. Kept for the browser session by `orders.create` and
|
|
5
|
+
* sent by every call that accepts it — `orders.get`, `orders.cancel`,
|
|
6
|
+
* `payments.checkout`, `payments.getForOrder`.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
/** cyrb53: a short, stable name for a value, so the value itself (a token, a
|
|
11
|
+
* buyer's details) is not what gets written as a storage key. */
|
|
12
|
+
export declare function nameOf(text: string): string;
|
|
13
|
+
/** Who the backend will take this request to be from: it keeps idempotency
|
|
14
|
+
* keys per signed-in account and per guest, so an unresolved attempt belongs
|
|
15
|
+
* to whoever made it and nobody else may reuse or clear it. */
|
|
16
|
+
export declare function actorOf(axios: AxiosInstance): string;
|
|
17
|
+
export declare function session(): Storage | null;
|
|
18
|
+
export declare function rememberOrderToken(axios: AxiosInstance, appId: string, actor: string, orderId: string, token?: string): void;
|
|
19
|
+
/** The header carrying this order's token: the one given, else the one kept. */
|
|
20
|
+
export declare function orderAccessHeaders(axios: AxiosInstance, appId: string, orderId: string, accessToken?: string): Record<string, string>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An order's access token (`X-Doany-Access-Token`): what lets a guest come back
|
|
3
|
+
* to an order they placed. Kept for the browser session by `orders.create` and
|
|
4
|
+
* sent by every call that accepts it — `orders.get`, `orders.cancel`,
|
|
5
|
+
* `payments.checkout`, `payments.getForOrder`.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
/** cyrb53: a short, stable name for a value, so the value itself (a token, a
|
|
10
|
+
* buyer's details) is not what gets written as a storage key. */
|
|
11
|
+
export function nameOf(text) {
|
|
12
|
+
let h1 = 0xdeadbeef;
|
|
13
|
+
let h2 = 0x41c6ce57;
|
|
14
|
+
for (let i = 0; i < text.length; i++) {
|
|
15
|
+
const ch = text.charCodeAt(i);
|
|
16
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
17
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
18
|
+
}
|
|
19
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
20
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
21
|
+
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36);
|
|
22
|
+
}
|
|
23
|
+
/** Who the backend will take this request to be from: it keeps idempotency
|
|
24
|
+
* keys per signed-in account and per guest, so an unresolved attempt belongs
|
|
25
|
+
* to whoever made it and nobody else may reuse or clear it. */
|
|
26
|
+
export function actorOf(axios) {
|
|
27
|
+
var _a, _b, _c;
|
|
28
|
+
const header = (_c = (_b = (_a = axios.defaults) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b.common) === null || _c === void 0 ? void 0 : _c["Authorization"];
|
|
29
|
+
if (typeof header !== "string" || !header.startsWith("Bearer "))
|
|
30
|
+
return "guest";
|
|
31
|
+
try {
|
|
32
|
+
const payload = header.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
33
|
+
const sub = JSON.parse(atob(payload)).sub;
|
|
34
|
+
if (typeof sub === "string" && sub)
|
|
35
|
+
return `user:${sub}`;
|
|
36
|
+
}
|
|
37
|
+
catch (_d) {
|
|
38
|
+
/* not a JWT we can read */
|
|
39
|
+
}
|
|
40
|
+
return `token:${nameOf(header)}`;
|
|
41
|
+
}
|
|
42
|
+
export function session() {
|
|
43
|
+
try {
|
|
44
|
+
return typeof window !== "undefined" ? window.sessionStorage : null;
|
|
45
|
+
}
|
|
46
|
+
catch (_a) {
|
|
47
|
+
return null; // storage can be blocked (private mode, sandboxed iframe)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// A kept token belongs to whoever placed the order. It is stored under that
|
|
51
|
+
// actor and looked up under the current one — plus `guest`, so an order placed
|
|
52
|
+
// before signing in can still be opened after. What it never does is follow
|
|
53
|
+
// the tab to another account, or stay usable once its account signed out.
|
|
54
|
+
function tokenKey(axios, appId, actor, orderId) {
|
|
55
|
+
var _a, _b;
|
|
56
|
+
return `doany_order_token_${appId}_${nameOf(`${(_b = (_a = axios.defaults) === null || _a === void 0 ? void 0 : _a.baseURL) !== null && _b !== void 0 ? _b : ""}|${actor}`)}_${orderId}`;
|
|
57
|
+
}
|
|
58
|
+
export function rememberOrderToken(axios, appId, actor, orderId, token) {
|
|
59
|
+
var _a;
|
|
60
|
+
if (!token)
|
|
61
|
+
return;
|
|
62
|
+
try {
|
|
63
|
+
(_a = session()) === null || _a === void 0 ? void 0 : _a.setItem(tokenKey(axios, appId, actor, orderId), token);
|
|
64
|
+
}
|
|
65
|
+
catch (_b) {
|
|
66
|
+
/* best effort */
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** The header carrying this order's token: the one given, else the one kept. */
|
|
70
|
+
export function orderAccessHeaders(axios, appId, orderId, accessToken) {
|
|
71
|
+
var _a, _b;
|
|
72
|
+
let token = accessToken;
|
|
73
|
+
if (!token) {
|
|
74
|
+
try {
|
|
75
|
+
const store = session();
|
|
76
|
+
token =
|
|
77
|
+
(_b = (_a = store === null || store === void 0 ? void 0 : store.getItem(tokenKey(axios, appId, actorOf(axios), orderId))) !== null && _a !== void 0 ? _a : store === null || store === void 0 ? void 0 : store.getItem(tokenKey(axios, appId, "guest", orderId))) !== null && _b !== void 0 ? _b : undefined;
|
|
78
|
+
}
|
|
79
|
+
catch (_c) {
|
|
80
|
+
token = undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return token ? { "X-Doany-Access-Token": token } : {};
|
|
84
|
+
}
|
package/dist/modules/orders.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AxiosInstance } from "axios";
|
|
2
2
|
import { OrdersModule } from "./orders.types";
|
|
3
|
+
import { ProjectScope } from "./project.js";
|
|
3
4
|
/** @internal Tests start from a page that has placed nothing. */
|
|
4
5
|
export declare function resetOrderAttempts(): void;
|
|
5
6
|
/**
|
|
@@ -14,8 +15,11 @@ export declare function resetOrderAttempts(): void;
|
|
|
14
15
|
* the same order — also after a page reload, for 15 minutes. Once the
|
|
15
16
|
* server has answered, the next identical order is a new one.
|
|
16
17
|
* - **A guest can reopen their order.** The order's access token is kept for
|
|
17
|
-
* the browser session and sent with `get` / `cancel` for that order
|
|
18
|
+
* the browser session and sent with `get` / `cancel` for that order, and
|
|
19
|
+
* with `payments.checkout` / `payments.getForOrder` (order-access.ts).
|
|
18
20
|
*
|
|
19
21
|
* @internal
|
|
20
22
|
*/
|
|
21
|
-
export declare function createOrdersModule(axios: AxiosInstance, appId: string
|
|
23
|
+
export declare function createOrdersModule(axios: AxiosInstance, appId: string, project: ProjectScope, { rememberAttempts, }?: {
|
|
24
|
+
rememberAttempts?: boolean;
|
|
25
|
+
}): OrdersModule;
|