lumnisai 0.5.51 → 0.5.53

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.cjs CHANGED
@@ -970,6 +970,7 @@ class ContactRelationshipsResource {
970
970
  }
971
971
  }
972
972
 
973
+ const COMPANY_SEARCH_PASSTHROUGH_KEYS = ["filters", "properties", "owners"];
973
974
  class CrmResource {
974
975
  constructor(http) {
975
976
  this.http = http;
@@ -1069,6 +1070,134 @@ class CrmResource {
1069
1070
  data
1070
1071
  );
1071
1072
  }
1073
+ /**
1074
+ * List the company fields the connected CRM exposes, with the operators each
1075
+ * one accepts — what a filter builder needs before calling
1076
+ * {@link searchCompanies}. A live, read-only provider call: nothing is
1077
+ * written to the CRM and nothing is served from the `crm_contacts` ledger.
1078
+ *
1079
+ * Pass `propertyName` to narrow to a single field. Attio returns
1080
+ * `select`/`status` choices only that way; HubSpot already includes
1081
+ * enumeration choices in the full listing, except for externally-sourced
1082
+ * ones, which come back as `options: null, optionsComplete: false`.
1083
+ *
1084
+ * Field metadata is provider-shaped, so the return type narrows on the
1085
+ * `provider` you pass.
1086
+ *
1087
+ * A definition with `references: 'owner'` holds a CRM user id; request it
1088
+ * in {@link searchCompanies} to have each company's `owners` map resolve
1089
+ * it to a name and email. HubSpot's is `hubspot_owner_id`; Attio has no
1090
+ * standard one, so take the actor-reference slug this listing flags.
1091
+ *
1092
+ * Failure modes: `403 crm_access_denied` and `503 crm_access_unavailable`
1093
+ * (the `crmUserId` grant), `409 crm_not_connected` (no single active
1094
+ * connection for the owner), `404 crm_property_not_found`,
1095
+ * `429 crm_rate_limited`, `504 crm_read_timeout`, `502 crm_read_failed`.
1096
+ *
1097
+ * @example
1098
+ * ```typescript
1099
+ * const { properties } = await client.crm.getCompanyProperties({
1100
+ * userId: 'user@example.com',
1101
+ * provider: 'hubspot',
1102
+ * })
1103
+ * const filterable = properties.filter(p => p.operators.length > 0)
1104
+ *
1105
+ * // Attio choices need the field named explicitly.
1106
+ * const tier = await client.crm.getCompanyProperties({
1107
+ * userId: 'user@example.com',
1108
+ * provider: 'attio',
1109
+ * propertyName: 'employee_range',
1110
+ * })
1111
+ * console.log(tier.properties[0].options)
1112
+ *
1113
+ * // The field that can fill an owner column.
1114
+ * const ownerField = properties.find(p => p.references === 'owner')
1115
+ * ```
1116
+ */
1117
+ async getCompanyProperties(params) {
1118
+ const response = await this.http.get(
1119
+ "/crm/companies/properties",
1120
+ {
1121
+ // Backend requires snake_case query params (see getContactsSyncStatus).
1122
+ params: {
1123
+ user_id: params.userId,
1124
+ provider: params.provider,
1125
+ property_name: params.propertyName,
1126
+ crm_user_id: params.crmUserId
1127
+ }
1128
+ }
1129
+ );
1130
+ return response;
1131
+ }
1132
+ /**
1133
+ * Read one page of companies from the connected CRM using that provider's
1134
+ * own filter syntax. Read-only: no CRM writes, no local persistence, so the
1135
+ * same request can be replayed to refresh a preview.
1136
+ *
1137
+ * Filters are never translated. HubSpot takes `filterGroups` (AND within a
1138
+ * group, OR between groups) with string comparison values; Attio takes its
1139
+ * record-query object with `$`-prefixed operators. Both cross the wire
1140
+ * verbatim — the SDK's camelCase ↔ snake_case conversion is switched off for
1141
+ * `filters` and for each company's `properties` and `owners` maps, whose
1142
+ * keys are CRM property names. Discover valid names and operators with
1143
+ * {@link getCompanyProperties}.
1144
+ *
1145
+ * Requesting an owner-typed field (`references: 'owner'` in its definition)
1146
+ * also resolves it: `owners[field]` is the CRM user's `{ id, name, email }`,
1147
+ * null when the company has none, with `name`/`email` null when the id is
1148
+ * not in the user directory. Resolution is part of the page — if the
1149
+ * directory read fails the whole request fails with the codes below rather
1150
+ * than returning half-resolved owners. Cost: any requested field adds one
1151
+ * definitions read per page; an owner field adds one directory read on top.
1152
+ *
1153
+ * Paging is cursor-based: pass the previous page's `nextCursor` back as
1154
+ * `cursor` and keep every other field identical, because HubSpot pages by
1155
+ * record id inside the original query. `nextCursor: null` is the last page.
1156
+ * `total` is reported on the first HubSpot page only, and is always null for
1157
+ * Attio.
1158
+ *
1159
+ * An empty `companies` array is a real "no matches" result — provider
1160
+ * failures raise instead. Failure modes: `403 crm_access_denied` and
1161
+ * `503 crm_access_unavailable` (the `crmUserId` grant),
1162
+ * `409 crm_not_connected`, `400 invalid_crm_filter` (the provider rejected
1163
+ * the query), `422 invalid_crm_request` (filter/limit/cursor budgets,
1164
+ * checked before the provider call), `429 crm_rate_limited`,
1165
+ * `504 crm_read_timeout`, `502 crm_read_failed`.
1166
+ *
1167
+ * @example
1168
+ * ```typescript
1169
+ * const request: CrmHubspotCompanySearchRequest = {
1170
+ * userId: 'user@example.com',
1171
+ * provider: 'hubspot',
1172
+ * filters: {
1173
+ * filterGroups: [{
1174
+ * filters: [
1175
+ * { propertyName: 'domain', operator: 'CONTAINS_TOKEN', value: 'acme' },
1176
+ * { propertyName: 'numberofemployees', operator: 'GT', value: '50' },
1177
+ * ],
1178
+ * }],
1179
+ * },
1180
+ * properties: ['numberofemployees', 'industry', 'hubspot_owner_id'],
1181
+ * limit: 50,
1182
+ * }
1183
+ *
1184
+ * const page = await client.crm.searchCompanies(request)
1185
+ * for (const company of page.companies)
1186
+ * console.log(company.name, company.properties.industry, company.owners?.hubspot_owner_id?.name)
1187
+ *
1188
+ * // Same query, next page.
1189
+ * if (page.nextCursor)
1190
+ * await client.crm.searchCompanies({ ...request, cursor: page.nextCursor })
1191
+ * ```
1192
+ */
1193
+ async searchCompanies(data) {
1194
+ const response = await this.http.post(
1195
+ "/crm/companies/search",
1196
+ data,
1197
+ { passthroughKeys: COMPANY_SEARCH_PASSTHROUGH_KEYS }
1198
+ );
1199
+ return response;
1200
+ }
1072
1201
  /**
1073
1202
  * Trigger a full mirror of the owner's CRM contact book into the local
1074
1203
  * `crm_contacts` ledger. Returns immediately (`202`); poll
@@ -1950,23 +2079,28 @@ const PASSTHROUGH_VALUE_KEYS = /* @__PURE__ */ new Set([
1950
2079
  // those would rename the customer's own groups.
1951
2080
  "committee"
1952
2081
  ]);
1953
- function convertCase(obj, converter) {
2082
+ function convertCase(obj, converter, passthroughKeys) {
1954
2083
  if (Array.isArray(obj)) {
1955
- return obj.map((v) => convertCase(v, converter));
2084
+ return obj.map((v) => convertCase(v, converter, passthroughKeys));
1956
2085
  } else if (obj !== null && typeof obj === "object") {
1957
2086
  return Object.keys(obj).reduce((acc, key) => {
1958
- const value = PASSTHROUGH_VALUE_KEYS.has(key) ? obj[key] : convertCase(obj[key], converter);
2087
+ const value = passthroughKeys.has(key) ? obj[key] : convertCase(obj[key], converter, passthroughKeys);
1959
2088
  acc[converter(key)] = value;
1960
2089
  return acc;
1961
2090
  }, {});
1962
2091
  }
1963
2092
  return obj;
1964
2093
  }
1965
- function toCamelCase(obj) {
1966
- return convertCase(obj, toCamel);
2094
+ function passthroughSet(extraKeys) {
2095
+ if (!extraKeys || extraKeys.length === 0)
2096
+ return PASSTHROUGH_VALUE_KEYS;
2097
+ return /* @__PURE__ */ new Set([...PASSTHROUGH_VALUE_KEYS, ...extraKeys]);
1967
2098
  }
1968
- function toSnakeCase(obj) {
1969
- return convertCase(obj, toSnake);
2099
+ function toCamelCase(obj, extraPassthroughKeys) {
2100
+ return convertCase(obj, toCamel, passthroughSet(extraPassthroughKeys));
2101
+ }
2102
+ function toSnakeCase(obj, extraPassthroughKeys) {
2103
+ return convertCase(obj, toSnake, passthroughSet(extraPassthroughKeys));
1970
2104
  }
1971
2105
 
1972
2106
  class MessagingResource {
@@ -5990,7 +6124,7 @@ class Http {
5990
6124
  backoffFactor: options.backoffFactor || DEFAULT_BACKOFF_FACTOR
5991
6125
  };
5992
6126
  }
5993
- async _handleResponse(response) {
6127
+ async _handleResponse(response, passthroughKeys) {
5994
6128
  const requestId = response.headers.get("x-request-id");
5995
6129
  if (response.ok) {
5996
6130
  if (response.status === 204)
@@ -5998,7 +6132,7 @@ class Http {
5998
6132
  const contentType = response.headers.get("content-type") || "";
5999
6133
  if (contentType.includes("application/json")) {
6000
6134
  const json = await response.json();
6001
- return toCamelCase(json);
6135
+ return toCamelCase(json, passthroughKeys);
6002
6136
  }
6003
6137
  return await response.text();
6004
6138
  }
@@ -6028,7 +6162,13 @@ class Http {
6028
6162
  }
6029
6163
  }
6030
6164
  async request(path, init = {}) {
6031
- const { body, params, idempotencyKey: idempotencyKeyOption, ...fetchOptions } = init;
6165
+ const {
6166
+ body,
6167
+ params,
6168
+ idempotencyKey: idempotencyKeyOption,
6169
+ passthroughKeys,
6170
+ ...fetchOptions
6171
+ } = init;
6032
6172
  const method = fetchOptions.method || "GET";
6033
6173
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
6034
6174
  const fullPath = this.options.apiPrefix ? `${this.options.apiPrefix}${normalizedPath}` : normalizedPath;
@@ -6057,10 +6197,10 @@ class Http {
6057
6197
  ...fetchOptions,
6058
6198
  method,
6059
6199
  headers,
6060
- body: body ? JSON.stringify(toSnakeCase(body)) : void 0,
6200
+ body: body ? JSON.stringify(toSnakeCase(body, passthroughKeys)) : void 0,
6061
6201
  signal: controller.signal
6062
6202
  });
6063
- return await this._handleResponse(response);
6203
+ return await this._handleResponse(response, passthroughKeys);
6064
6204
  } catch (error) {
6065
6205
  lastError = error;
6066
6206
  if (error instanceof RateLimitError) {