@doany-ai/sdk 0.2.7 → 0.2.8

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 CHANGED
@@ -202,6 +202,9 @@ export function createClient(config) {
202
202
  return {
203
203
  getCheckoutSession: full.getCheckoutSession.bind(full),
204
204
  getSubscription: full.getSubscription.bind(full),
205
+ // Reading the products is not a decision for any browser, and a
206
+ // fulfilment function often needs what it just sold.
207
+ products: full.products,
205
208
  };
206
209
  })(),
207
210
  functions: createFunctionsModule(serviceRoleFunctionsAxiosClient, appId, {
@@ -457,6 +460,21 @@ export function createClientFromRequest(request) {
457
460
  if (runIdHeader) {
458
461
  additionalHeaders["X-Run-Id"] = runIdHeader.slice(0, 128);
459
462
  }
463
+ // Workflow diagnostics are separate from Annie's X-Run-Id attribution.
464
+ // The function proxy owns these headers; neither their presence nor their
465
+ // contents grant permissions or change billing. Match its bounded format.
466
+ if (serviceRoleToken) {
467
+ for (const name of [
468
+ "X-Doany-Workflow-Id",
469
+ "X-Doany-Workflow-Run-Id",
470
+ "X-Doany-Workflow-Step-Id",
471
+ ]) {
472
+ const value = request.headers.get(name);
473
+ if (value && /^[A-Za-z0-9_.:/-]{1,128}$/.test(value)) {
474
+ additionalHeaders[name] = value;
475
+ }
476
+ }
477
+ }
460
478
  return createClient({
461
479
  serverUrl: serverUrlHeader || "https://api.doany.ai",
462
480
  appId,
@@ -150,10 +150,11 @@ export interface DoanyClient {
150
150
  * service-role caller, so the ordinary client cannot resolve a mode and
151
151
  * the read fails whatever the function forwards.
152
152
  *
153
- * Only the reads are here. There is no service-role checkout — opening one
154
- * is a decision that belongs to the browser that is actually there.
153
+ * Only the reads are here — the two above and the products. There
154
+ * is no service-role checkout: opening one is a decision that belongs to
155
+ * the browser that is actually there.
155
156
  */
156
- payments: Pick<PaymentsModule, "getCheckoutSession" | "getSubscription">;
157
+ payments: Pick<PaymentsModule, "getCheckoutSession" | "getSubscription" | "products">;
157
158
  /** {@link SsoModule | SSO module} for generating SSO tokens.
158
159
  * @internal
159
160
  */
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export type { FunctionsModule, FunctionName, FunctionNameRegistry, } from "./mod
11
11
  export type { AgentsModule, AgentName, AgentNameRegistry, AgentConversation, AgentMessage, AgentMessageReasoning, AgentMessageToolCall, AgentMessageUsage, AgentMessageCustomContext, AgentMessageMetadata, CreateConversationParams, } from "./modules/agents.types.js";
12
12
  export type { AiGatewayModule, AiGatewayConnection, } from "./modules/ai-gateway.types.js";
13
13
  export type { AppLogsModule } from "./modules/app-logs.types.js";
14
- export type { PaymentsModule, CheckoutLineItem, CreateCheckoutParams, CreateCheckoutResult, CreateEmbeddedCheckoutResult, CheckoutSession, SubscriptionState, BillingPortalParams, } from "./modules/payments.types.js";
14
+ export type { PaymentsModule, ProductsModule, Product, ProductQuery, ProductSort, CheckoutLineItem, CreateCheckoutParams, CreateCheckoutResult, CreateEmbeddedCheckoutResult, CheckoutSession, SubscriptionState, BillingPortalParams, } from "./modules/payments.types.js";
15
15
  export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
16
16
  export type { ConnectorsModule, UserConnectorsModule, ConnectorApiRequest, ConnectorApiResponse, ConnectorProxyRawResponse, } from "./modules/connectors.types.js";
17
17
  export type { CustomIntegrationsModule, CustomIntegrationCallParams, CustomIntegrationCallResponse, } from "./modules/custom-integrations.types.js";
@@ -1,5 +1,16 @@
1
1
  import { AxiosInstance } from "axios";
2
- import { PaymentsModule } from "./payments.types";
2
+ import { PaymentsModule, ProductsModule } from "./payments.types";
3
+ /**
4
+ * The product reads.
5
+ *
6
+ * Parameters go over the wire exactly as an entity's do (`sort`, `limit`,
7
+ * `skip`, `q` as JSON), because the point of this module is that code written
8
+ * against `entities.Product` moves over by renaming it — same arguments, same
9
+ * records back.
10
+ *
11
+ * @internal
12
+ */
13
+ export declare function createProductsModule(axios: AxiosInstance, appId: string): ProductsModule;
3
14
  /**
4
15
  * Creates the payments module for the Doany SDK.
5
16
  *
@@ -1,3 +1,42 @@
1
+ /**
2
+ * The product reads.
3
+ *
4
+ * Parameters go over the wire exactly as an entity's do (`sort`, `limit`,
5
+ * `skip`, `q` as JSON), because the point of this module is that code written
6
+ * against `entities.Product` moves over by renaming it — same arguments, same
7
+ * records back.
8
+ *
9
+ * @internal
10
+ */
11
+ export function createProductsModule(axios, appId) {
12
+ const baseURL = `/apps/${appId}/payments/products`;
13
+ function params(sort, limit, skip) {
14
+ const out = {};
15
+ if (sort)
16
+ out.sort = sort;
17
+ if (limit)
18
+ out.limit = limit;
19
+ if (skip)
20
+ out.skip = skip;
21
+ return out;
22
+ }
23
+ return {
24
+ async list(sort, limit, skip) {
25
+ const data = await axios.get(baseURL, { params: params(sort, limit, skip) });
26
+ return data;
27
+ },
28
+ async filter(query, sort, limit, skip) {
29
+ const data = await axios.get(baseURL, {
30
+ params: { q: JSON.stringify(query), ...params(sort, limit, skip) },
31
+ });
32
+ return data;
33
+ },
34
+ async get(id) {
35
+ const data = await axios.get(`${baseURL}/${encodeURIComponent(id)}`);
36
+ return data;
37
+ },
38
+ };
39
+ }
1
40
  /**
2
41
  * Creates the payments module for the Doany SDK.
3
42
  *
@@ -21,6 +60,7 @@ export function createPaymentsModule(axios, appId) {
21
60
  // Axios's declared return type does not reflect that, so the results below
22
61
  // are cast through `unknown`.
23
62
  return {
63
+ products: createProductsModule(axios, appId),
24
64
  createCheckoutSession,
25
65
  async getSubscription(subscriptionId) {
26
66
  const data = await axios.request({
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * One thing being sold in a checkout.
3
3
  *
4
- * A line item names a record in your own app data — doany keeps your catalog
4
+ * A line item names a record in your own app data — doany keeps your products
5
5
  * there rather than as objects inside Stripe, which is why switching from test
6
6
  * to real payments needs no migration.
7
7
  */
8
8
  export type CheckoutLineItem = {
9
9
  /**
10
- * The `id` of a record in this app's `Product` entity.
10
+ * The `id` of one of this app's products — a record from
11
+ * {@linkcode ProductsModule | doany.payments.products}, or from the `Product`
12
+ * entity on an app whose products predate it.
11
13
  *
12
14
  * The price, name and currency all come from that record — you cannot pass
13
15
  * a price. This endpoint is reachable by anyone (a shop has to sell to
@@ -65,7 +67,7 @@ export type CreateCheckoutResult = {
65
67
  /** `test` in the preview, `live` on a published site that has gone live. */
66
68
  mode: "test" | "live";
67
69
  /**
68
- * What the customer is about to agree to, decided by the catalog.
70
+ * What the customer is about to agree to, decided by the product.
69
71
  *
70
72
  * `subscription` when the products carry a `recurring_interval`, `payment`
71
73
  * otherwise. Say the right word on the button: "Subscribe" over a one-off
@@ -203,17 +205,98 @@ export type CheckoutSession = {
203
205
  */
204
206
  access_token?: string;
205
207
  };
208
+ /**
209
+ * One of this app's products, as {@linkcode ProductsModule} returns it.
210
+ *
211
+ * The same shape an entity record has: the platform's fields and the system
212
+ * fields by their entity names, and every field of the app's own (`slug`,
213
+ * `category`, `image_url`, …) at the top level beside them.
214
+ */
215
+ export type Product = {
216
+ id: string;
217
+ name: string;
218
+ description: string | null;
219
+ /** Minor units — 2400 is 24.00. `null` for something shown but not sold. */
220
+ price_cents: number | null;
221
+ /** Lowercase ISO code, or `null` for the checkout's default. */
222
+ currency: string | null;
223
+ /** Present => a subscription billed this often. `null` => a one-off. */
224
+ recurring_interval: "week" | "month" | "year" | null;
225
+ /** `false` is off sale: still readable, cannot be bought. */
226
+ is_available: boolean;
227
+ created_date: string;
228
+ updated_date: string;
229
+ created_by: string | null;
230
+ created_by_id: string | null;
231
+ is_sample: boolean;
232
+ /**
233
+ * Only ever on {@linkcode ProductsModule.get}: the product was removed from
234
+ * the store. Kept readable because past orders and subscriptions name it.
235
+ */
236
+ is_deleted?: true;
237
+ /**
238
+ * The app's own fields. `any`, as on an entity record, so code that read
239
+ * `product.slug.toLowerCase()` off `entities.Product` still type-checks.
240
+ */
241
+ [field: string]: any;
242
+ };
243
+ /**
244
+ * A field to order by, `-` first for descending: `"price_cents"`,
245
+ * `"-created_date"`, or one of the app's own fields.
246
+ */
247
+ export type ProductSort = string;
248
+ /**
249
+ * Equality only — `{ slug: "starter" }`, `{ is_available: true }`. Several
250
+ * fields must all match. `{ field: null }` also matches a product that does not
251
+ * have the field. Operators (`$gt`, `$in`, …) are refused.
252
+ */
253
+ export type ProductQuery = Record<string, string | number | boolean | null>;
254
+ /**
255
+ * Read this app's products. Read-only: products are added and priced by
256
+ * the app owner in the Payments panel, or by asking Annie.
257
+ *
258
+ * Same methods, arguments and results as an entity's reads, so code written
259
+ * against `entities.Product` moves over by renaming it. Records come back in
260
+ * the order they were created when no `sort` is given.
261
+ */
262
+ export interface ProductsModule {
263
+ /**
264
+ * @example
265
+ * ```typescript
266
+ * const products = await doany.payments.products.list();
267
+ * const newest = await doany.payments.products.list('-created_date', 20);
268
+ * ```
269
+ */
270
+ list(sort?: ProductSort, limit?: number, skip?: number): Promise<Product[]>;
271
+ /**
272
+ * @example
273
+ * ```typescript
274
+ * const [plan] = await doany.payments.products.filter({ slug: 'pro' });
275
+ * const onSale = await doany.payments.products.filter(
276
+ * { is_available: true }, 'price_cents', 10,
277
+ * );
278
+ * ```
279
+ */
280
+ filter(query: ProductQuery, sort?: ProductSort, limit?: number, skip?: number): Promise<Product[]>;
281
+ /**
282
+ * One product by id. A removed product is still returned, with
283
+ * `is_deleted: true`; an id that was never a product is a 404.
284
+ */
285
+ get(id: string): Promise<Product>;
286
+ }
206
287
  /**
207
288
  * Take card payments on your site.
208
289
  *
209
290
  * Money goes to the app owner's own Stripe account — doany never holds it and
210
291
  * takes no cut. Stripe's usual per-transaction fee applies.
211
292
  *
212
- * ## Prices live in your data, not in your code
293
+ * ## Prices live with the product, not in your code
213
294
  *
214
- * Sellable things are records in this app's `Product` entity, with the price in
215
- * an integer `price_cents` field. A checkout names the product; the server
216
- * looks up what it costs.
295
+ * Sellable things are this app's products, read with
296
+ * {@linkcode PaymentsModule.products}, with the price in an integer
297
+ * `price_cents` field. A checkout names the product; the server looks up what
298
+ * it costs. (An app whose products predate this keeps them in a `Product`
299
+ * entity; checkout reads whichever one the app uses.)
217
300
  *
218
301
  * ## Test and real payments
219
302
  *
@@ -229,12 +312,14 @@ export type CheckoutSession = {
229
312
  * The same code covers both. There is no key to configure and no mode to set.
230
313
  */
231
314
  export interface PaymentsModule {
315
+ /** This app's products. See {@linkcode ProductsModule}. */
316
+ products: ProductsModule;
232
317
  /**
233
318
  * Opens a Stripe checkout and returns the URL to send the customer to.
234
319
  *
235
320
  * @example Sell one item
236
321
  * ```typescript
237
- * // The price comes from the Product record, not from this call.
322
+ * // The price comes from the product, not from this call.
238
323
  * const { url } = await doany.payments.createCheckoutSession({
239
324
  * line_items: [{ product_id: product.id, quantity: 1 }],
240
325
  * success_path: '/thanks',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doany-ai/sdk",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "JavaScript SDK for the doany app platform (API-compatible fork of @base44/sdk)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",