@syncmatters/connector-sdk 1.0.8 → 1.0.9

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.
@@ -70,6 +70,7 @@ same as "no list support". See [the query capability bar](#the-query-capability-
70
70
  | `type` | `"string"` · `"number"` · `"boolean"` · `"object"` · `"array"` · `"file"` · `"any"` |
71
71
  | `isKey: true` | this field is the row's unique id — its value becomes `row.meta.key`. Assign this (or `isUserKey`) to exactly one `queryFields`, plus one `upsertFields`, plus one `deleteFields` |
72
72
  | `isUserKey: true` | user-supplied natural key that can both add and update — see below |
73
+ | `isAlternateKey: true` | scalar `queryFields` field holding a unique value the connector can resolve rows by (`queryOptions.idsFilterField`) — a COMMITMENT to honor that filter, see [05-query.md](./05-query.md) |
73
74
  | `canMatch: true` | field may be referenced by match rules (set on id/name/email-like fields; apply recursively into nested object fields where meaningful) |
74
75
  | `optionValues` | picklist options `[{ id, name?, group? }]` |
75
76
  | `canUpdateOptions: true` | new picklist options may be added via `upsertFieldOptions()` |
package/docs/05-query.md CHANGED
@@ -84,6 +84,7 @@ Rules that follow from the lifecycle:
84
84
  | `queryOptions.fields` | field paths the caller needs (used to fetch less when the API supports it) |
85
85
  | `queryOptions.file` | LEGACY - ignore; modern connectors include file content when `fields` contains a file-typed field's path (13) |
86
86
  | `queryOptions.idsFilter` | `string[]` of row keys — return exactly those rows; row with the key not found? omit it, do not report an error |
87
+ | `queryOptions.idsFilterField` | path of the alternate key field the `idsFilter` values refer to (absent = internal row id) — below |
87
88
  | `queryOptions.checkpointFilter` | `{ value?: string }` — rows changed since your last checkpoint (undefined value = first differential run: return everything) |
88
89
  | `queryOptions.matchFilter` | find rows matching people/records — below |
89
90
  | `queryOptions.relatedFilter` | rows related to another object's rows — below |
@@ -106,6 +107,40 @@ null/empty ids so you never emit `id = 'undefined'`.
106
107
 
107
108
  When the row does not exist on the target system (e.g. deleted), do not report an error (e.g. API returns 404), simply do not return the row.
108
109
 
110
+ ### idsFilterField (alternate key lookup)
111
+
112
+ When `queryOptions.idsFilterField` accompanies `idsFilter`, the ids are NOT row keys — they are
113
+ values of the alternate key field at that path (a field you flagged `isAlternateKey: true` in
114
+ `queryFields`). The platform only sends this option for flagged fields, so flagging a field is
115
+ your commitment to honor it. The contract:
116
+
117
+ - return only rows that matched one of the requested values (unmatched values: omit, do not
118
+ error — same as `idsFilter`)
119
+ - the platform correlates each returned row back to a requested value. Two ways to support that:
120
+ 1. **annotate the row**: set `row.relationship = { srcRowId: <requested value> }` — the same
121
+ annotation the related/match flows use. REQUIRED when a row can match via anything other
122
+ than the field's stored value verbatim (aliases such as HubSpot's `hs_additional_emails`,
123
+ case-insensitive resolution). A row that matched multiple requested values is returned once
124
+ per value, each annotated.
125
+ 2. **rely on the field value**: when matching is exact, it is enough to include the alternate
126
+ key field in each returned row's `data` (even when the caller narrowed the response with
127
+ `fields`) — the platform reads the value from the row.
128
+ - `row.meta.key` remains the REAL internal row id, never the alternate value
129
+
130
+ ```js
131
+ const path = options.queryOptions?.idsFilterField;
132
+ if (path) {
133
+ const propName = path[path.length - 1]?.path; // e.g. properties.my_unique_prop -> my_unique_prop
134
+ // resolve via your alternate-id capable endpoint (e.g. HubSpot batch-read with idProperty),
135
+ // then annotate each row with the requested value it matched
136
+ }
137
+ ```
138
+
139
+ You MUST throw `SDK.ErrorCode.InvalidFilterParameter` if the path does not resolve to a field
140
+ you can retrieve by (unknown, or no longer unique — possible when stored metadata is newer than
141
+ the deployed connector). The platform does NOT pre-validate this option — your throw is the only
142
+ thing standing between a bad path and silently empty results.
143
+
109
144
  ### checkpointFilter (differential sync)
110
145
 
111
146
  The contract is a round-trip: you emit `checkpoint` (any string — ISO date is conventional) on
@@ -110,6 +110,8 @@ export interface QueryOptions {
110
110
  randomFilter?: boolean;
111
111
  /** filter for rows by row id (key) */
112
112
  idsFilter?: Array<string>;
113
+ /** path of the alternate key field that the idsFilter values refer to, if applicable */
114
+ idsFilterField?: Array<JsonValuePathPart>;
113
115
  /** filter for rows related to other rows */
114
116
  relatedFilter?: QueryRelatedFilter;
115
117
  /** filter for rows that meet a specific matching rule */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncmatters/connector-sdk",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "TypeScript type definitions for the SyncMatters connector SDK (types only - connectors execute on the SyncMatters platform)",
5
5
  "types": "./index.d.ts",
6
6
  "exports": {
@@ -12,9 +12,9 @@
12
12
  "license": "MIT",
13
13
  "author": "SyncMatters",
14
14
  "homepage": "https://syncmatters.com",
15
- "typesContentHash": "38cad56617a6326dd04e6b8d467217d541bcc950f4aaea59ef04f7186caa7bfb",
15
+ "typesContentHash": "bc9f5a1637b46d39ef65a04b24338624b3c51f60df16d6646113067bdec0aa86",
16
16
  "dependencies": {
17
17
  "@types/node": "*",
18
- "@syncmatters/script-api": "^1.0.6"
18
+ "@syncmatters/script-api": "^1.0.9"
19
19
  }
20
20
  }