lumnisai 0.5.50 → 0.5.52

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"];
973
974
  class CrmResource {
974
975
  constructor(http) {
975
976
  this.http = http;
@@ -1069,6 +1070,118 @@ 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
+ * Failure modes: `403 crm_access_denied` and `503 crm_access_unavailable`
1088
+ * (the `crmUserId` grant), `409 crm_not_connected` (no single active
1089
+ * connection for the owner), `404 crm_property_not_found`,
1090
+ * `429 crm_rate_limited`, `504 crm_read_timeout`, `502 crm_read_failed`.
1091
+ *
1092
+ * @example
1093
+ * ```typescript
1094
+ * const { properties } = await client.crm.getCompanyProperties({
1095
+ * userId: 'user@example.com',
1096
+ * provider: 'hubspot',
1097
+ * })
1098
+ * const filterable = properties.filter(p => p.operators.length > 0)
1099
+ *
1100
+ * // Attio choices need the field named explicitly.
1101
+ * const tier = await client.crm.getCompanyProperties({
1102
+ * userId: 'user@example.com',
1103
+ * provider: 'attio',
1104
+ * propertyName: 'employee_range',
1105
+ * })
1106
+ * console.log(tier.properties[0].options)
1107
+ * ```
1108
+ */
1109
+ async getCompanyProperties(params) {
1110
+ const response = await this.http.get(
1111
+ "/crm/companies/properties",
1112
+ {
1113
+ // Backend requires snake_case query params (see getContactsSyncStatus).
1114
+ params: {
1115
+ user_id: params.userId,
1116
+ provider: params.provider,
1117
+ property_name: params.propertyName,
1118
+ crm_user_id: params.crmUserId
1119
+ }
1120
+ }
1121
+ );
1122
+ return response;
1123
+ }
1124
+ /**
1125
+ * Read one page of companies from the connected CRM using that provider's
1126
+ * own filter syntax. Read-only: no CRM writes, no local persistence, so the
1127
+ * same request can be replayed to refresh a preview.
1128
+ *
1129
+ * Filters are never translated. HubSpot takes `filterGroups` (AND within a
1130
+ * group, OR between groups) with string comparison values; Attio takes its
1131
+ * record-query object with `$`-prefixed operators. Both cross the wire
1132
+ * verbatim — the SDK's camelCase ↔ snake_case conversion is switched off for
1133
+ * `filters` and for each company's `properties` map, whose keys are CRM
1134
+ * property names. Discover valid names and operators with
1135
+ * {@link getCompanyProperties}.
1136
+ *
1137
+ * Paging is cursor-based: pass the previous page's `nextCursor` back as
1138
+ * `cursor` and keep every other field identical, because HubSpot pages by
1139
+ * record id inside the original query. `nextCursor: null` is the last page.
1140
+ * `total` is reported on the first HubSpot page only, and is always null for
1141
+ * Attio.
1142
+ *
1143
+ * An empty `companies` array is a real "no matches" result — provider
1144
+ * failures raise instead. Failure modes: `403 crm_access_denied` and
1145
+ * `503 crm_access_unavailable` (the `crmUserId` grant),
1146
+ * `409 crm_not_connected`, `400 invalid_crm_filter` (the provider rejected
1147
+ * the query), `422 invalid_crm_request` (filter/limit/cursor budgets,
1148
+ * checked before the provider call), `429 crm_rate_limited`,
1149
+ * `504 crm_read_timeout`, `502 crm_read_failed`.
1150
+ *
1151
+ * @example
1152
+ * ```typescript
1153
+ * const request: CrmHubspotCompanySearchRequest = {
1154
+ * userId: 'user@example.com',
1155
+ * provider: 'hubspot',
1156
+ * filters: {
1157
+ * filterGroups: [{
1158
+ * filters: [
1159
+ * { propertyName: 'domain', operator: 'CONTAINS_TOKEN', value: 'acme' },
1160
+ * { propertyName: 'numberofemployees', operator: 'GT', value: '50' },
1161
+ * ],
1162
+ * }],
1163
+ * },
1164
+ * properties: ['numberofemployees', 'industry'],
1165
+ * limit: 50,
1166
+ * }
1167
+ *
1168
+ * const page = await client.crm.searchCompanies(request)
1169
+ * for (const company of page.companies)
1170
+ * console.log(company.name, company.properties.industry)
1171
+ *
1172
+ * // Same query, next page.
1173
+ * if (page.nextCursor)
1174
+ * await client.crm.searchCompanies({ ...request, cursor: page.nextCursor })
1175
+ * ```
1176
+ */
1177
+ async searchCompanies(data) {
1178
+ const response = await this.http.post(
1179
+ "/crm/companies/search",
1180
+ data,
1181
+ { passthroughKeys: COMPANY_SEARCH_PASSTHROUGH_KEYS }
1182
+ );
1183
+ return response;
1184
+ }
1072
1185
  /**
1073
1186
  * Trigger a full mirror of the owner's CRM contact book into the local
1074
1187
  * `crm_contacts` ledger. Returns immediately (`202`); poll
@@ -1950,23 +2063,28 @@ const PASSTHROUGH_VALUE_KEYS = /* @__PURE__ */ new Set([
1950
2063
  // those would rename the customer's own groups.
1951
2064
  "committee"
1952
2065
  ]);
1953
- function convertCase(obj, converter) {
2066
+ function convertCase(obj, converter, passthroughKeys) {
1954
2067
  if (Array.isArray(obj)) {
1955
- return obj.map((v) => convertCase(v, converter));
2068
+ return obj.map((v) => convertCase(v, converter, passthroughKeys));
1956
2069
  } else if (obj !== null && typeof obj === "object") {
1957
2070
  return Object.keys(obj).reduce((acc, key) => {
1958
- const value = PASSTHROUGH_VALUE_KEYS.has(key) ? obj[key] : convertCase(obj[key], converter);
2071
+ const value = passthroughKeys.has(key) ? obj[key] : convertCase(obj[key], converter, passthroughKeys);
1959
2072
  acc[converter(key)] = value;
1960
2073
  return acc;
1961
2074
  }, {});
1962
2075
  }
1963
2076
  return obj;
1964
2077
  }
1965
- function toCamelCase(obj) {
1966
- return convertCase(obj, toCamel);
2078
+ function passthroughSet(extraKeys) {
2079
+ if (!extraKeys || extraKeys.length === 0)
2080
+ return PASSTHROUGH_VALUE_KEYS;
2081
+ return /* @__PURE__ */ new Set([...PASSTHROUGH_VALUE_KEYS, ...extraKeys]);
1967
2082
  }
1968
- function toSnakeCase(obj) {
1969
- return convertCase(obj, toSnake);
2083
+ function toCamelCase(obj, extraPassthroughKeys) {
2084
+ return convertCase(obj, toCamel, passthroughSet(extraPassthroughKeys));
2085
+ }
2086
+ function toSnakeCase(obj, extraPassthroughKeys) {
2087
+ return convertCase(obj, toSnake, passthroughSet(extraPassthroughKeys));
1970
2088
  }
1971
2089
 
1972
2090
  class MessagingResource {
@@ -5990,7 +6108,7 @@ class Http {
5990
6108
  backoffFactor: options.backoffFactor || DEFAULT_BACKOFF_FACTOR
5991
6109
  };
5992
6110
  }
5993
- async _handleResponse(response) {
6111
+ async _handleResponse(response, passthroughKeys) {
5994
6112
  const requestId = response.headers.get("x-request-id");
5995
6113
  if (response.ok) {
5996
6114
  if (response.status === 204)
@@ -5998,7 +6116,7 @@ class Http {
5998
6116
  const contentType = response.headers.get("content-type") || "";
5999
6117
  if (contentType.includes("application/json")) {
6000
6118
  const json = await response.json();
6001
- return toCamelCase(json);
6119
+ return toCamelCase(json, passthroughKeys);
6002
6120
  }
6003
6121
  return await response.text();
6004
6122
  }
@@ -6028,7 +6146,13 @@ class Http {
6028
6146
  }
6029
6147
  }
6030
6148
  async request(path, init = {}) {
6031
- const { body, params, idempotencyKey: idempotencyKeyOption, ...fetchOptions } = init;
6149
+ const {
6150
+ body,
6151
+ params,
6152
+ idempotencyKey: idempotencyKeyOption,
6153
+ passthroughKeys,
6154
+ ...fetchOptions
6155
+ } = init;
6032
6156
  const method = fetchOptions.method || "GET";
6033
6157
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
6034
6158
  const fullPath = this.options.apiPrefix ? `${this.options.apiPrefix}${normalizedPath}` : normalizedPath;
@@ -6057,10 +6181,10 @@ class Http {
6057
6181
  ...fetchOptions,
6058
6182
  method,
6059
6183
  headers,
6060
- body: body ? JSON.stringify(toSnakeCase(body)) : void 0,
6184
+ body: body ? JSON.stringify(toSnakeCase(body, passthroughKeys)) : void 0,
6061
6185
  signal: controller.signal
6062
6186
  });
6063
- return await this._handleResponse(response);
6187
+ return await this._handleResponse(response, passthroughKeys);
6064
6188
  } catch (error) {
6065
6189
  lastError = error;
6066
6190
  if (error instanceof RateLimitError) {