From 85e52280ddb6daf6c595d4f3c72aa90b5fd931d8 Mon Sep 17 00:00:00 2001 From: mariczne Date: Fri, 29 Aug 2025 10:22:33 +0200 Subject: [PATCH 1/2] Fix #18220 (airtable_oauth): normalize fallback events and retry getting record - Always wrap fallback emissions as { originalPayload } for consistent shape - Guard against missing/empty changedTablesById in field & record emitters - Add shared withRetry() (exponential backoff,) in common/utils - Use withRetry for getRecord in record emitter; on failure, emit empty fields --- components/airtable_oauth/common/utils.mjs | 20 +++++++++++ .../sources/common/common-webhook-field.mjs | 11 +++++- .../sources/common/common-webhook-record.mjs | 35 +++++++++++++++---- .../sources/common/common-webhook.mjs | 6 +++- 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/components/airtable_oauth/common/utils.mjs b/components/airtable_oauth/common/utils.mjs index f3dda1de77e5e..bff7ae4842980 100644 --- a/components/airtable_oauth/common/utils.mjs +++ b/components/airtable_oauth/common/utils.mjs @@ -163,9 +163,29 @@ function buildSingleCollaboratorField(value) { }; } +function sleep(ms) { + return new Promise((r) => setTimeout(r, ms)); +} + +async function withRetry(fn, { + retries = 2, baseDelay = 500, +} = {}) { + let attempt = 0; + while (attempt <= retries) { + try { + return await fn(); + } catch (err) { + if (attempt === retries) throw err; + await sleep(baseDelay * (2 ** attempt)); + attempt += 1; + } + } +} + export { fieldTypeToPropType, fieldToProp, makeFieldProps, makeRecord, + withRetry, }; diff --git a/components/airtable_oauth/sources/common/common-webhook-field.mjs b/components/airtable_oauth/sources/common/common-webhook-field.mjs index 90b206ab963eb..39f23218f41b0 100644 --- a/components/airtable_oauth/sources/common/common-webhook-field.mjs +++ b/components/airtable_oauth/sources/common/common-webhook-field.mjs @@ -25,10 +25,19 @@ export default { } }, async emitEvent(payload) { + const changed = payload?.changedTablesById; + const tableEntry = changed && Object.entries(changed)[0]; + if (!tableEntry) { + // Unknown / empty shape — emit normalized raw so consumers still get a consistent shape + this.$emit({ + originalPayload: payload, + }, this.generateMeta(payload)); + return; + } const [ tableId, tableData, - ] = Object.entries(payload.changedTablesById)[0]; + ] = tableEntry; const [ operation, fieldObj, diff --git a/components/airtable_oauth/sources/common/common-webhook-record.mjs b/components/airtable_oauth/sources/common/common-webhook-record.mjs index a610d402e830a..bbeb88e5ef982 100644 --- a/components/airtable_oauth/sources/common/common-webhook-record.mjs +++ b/components/airtable_oauth/sources/common/common-webhook-record.mjs @@ -1,4 +1,5 @@ import common from "./common-webhook.mjs"; +import { withRetry } from "../../common/utils.mjs"; export default { ...common, @@ -10,10 +11,19 @@ export default { ]; }, async emitEvent(payload) { + const changed = payload?.changedTablesById; + const tableEntry = changed && Object.entries(changed)[0]; + if (!tableEntry) { + // Unknown / empty shape — emit normalized raw so consumers still get a consistent shape + this.$emit({ + originalPayload: payload, + }, this.generateMeta(payload)); + return; + } const [ tableId, tableData, - ] = Object.entries(payload.changedTablesById)[0]; + ] = tableEntry; let [ operation, recordObj, @@ -52,11 +62,24 @@ export default { ? "created" : "updated"; - const { fields } = await this.airtable.getRecord({ - baseId: this.baseId, - tableId, - recordId, - }); + let fields = {}; + try { + const res = await withRetry( + () => this.airtable.getRecord({ + baseId: this.baseId, + tableId, + recordId, + }), + ); + + fields = res?.fields ?? {}; + } catch (err) { + console.log("Airtable getRecord failed; emitting empty fields", { + statusCode: err?.statusCode ?? err?.response?.status, + message: err?.message, + }); + fields = {}; + } const summary = `Record ${updateType}: ${fields?.name ?? recordId}`; diff --git a/components/airtable_oauth/sources/common/common-webhook.mjs b/components/airtable_oauth/sources/common/common-webhook.mjs index 8375fcc85fa22..2d8d58816504c 100644 --- a/components/airtable_oauth/sources/common/common-webhook.mjs +++ b/components/airtable_oauth/sources/common/common-webhook.mjs @@ -134,8 +134,12 @@ export default { return this.changeTypes; }, emitDefaultEvent(payload) { + // Normalize the fallback so consumers always get a consistent structure + // matching our other emitters (which include originalPayload). const meta = this.generateMeta(payload); - this.$emit(payload, meta); + this.$emit({ + originalPayload: payload, + }, meta); }, async emitEvent(payload) { // sources may call this to customize event emission, but it is From 3be22a2cc582da6415371286de64326b610edd81 Mon Sep 17 00:00:00 2001 From: mariczne Date: Fri, 29 Aug 2025 15:12:00 +0200 Subject: [PATCH 2/2] Bump package versions --- .../airtable_oauth/actions/create-comment/create-comment.mjs | 2 +- components/airtable_oauth/actions/create-field/create-field.mjs | 2 +- .../actions/create-multiple-records/create-multiple-records.mjs | 2 +- .../actions/create-or-update-record/create-or-update-record.mjs | 2 +- .../actions/create-single-record/create-single-record.mjs | 2 +- components/airtable_oauth/actions/create-table/create-table.mjs | 2 +- .../airtable_oauth/actions/delete-record/delete-record.mjs | 2 +- .../actions/get-record-or-create/get-record-or-create.mjs | 2 +- components/airtable_oauth/actions/get-record/get-record.mjs | 2 +- components/airtable_oauth/actions/list-bases/list-bases.mjs | 2 +- .../actions/list-records-in-view/list-records-in-view.mjs | 2 +- components/airtable_oauth/actions/list-records/list-records.mjs | 2 +- components/airtable_oauth/actions/list-tables/list-tables.mjs | 2 +- .../airtable_oauth/actions/search-records/search-records.mjs | 2 +- .../airtable_oauth/actions/update-comment/update-comment.mjs | 2 +- components/airtable_oauth/actions/update-field/update-field.mjs | 2 +- .../airtable_oauth/actions/update-record/update-record.mjs | 2 +- components/airtable_oauth/actions/update-table/update-table.mjs | 2 +- components/airtable_oauth/package.json | 2 +- components/airtable_oauth/sources/new-field/new-field.mjs | 2 +- .../new-modified-or-deleted-records-instant.mjs | 2 +- .../new-modified-or-deleted-records.mjs | 2 +- .../sources/new-or-modified-field/new-or-modified-field.mjs | 2 +- .../new-or-modified-records-in-view.mjs | 2 +- .../sources/new-or-modified-records/new-or-modified-records.mjs | 2 +- .../sources/new-records-in-view/new-records-in-view.mjs | 2 +- components/airtable_oauth/sources/new-records/new-records.mjs | 2 +- 27 files changed, 27 insertions(+), 27 deletions(-) diff --git a/components/airtable_oauth/actions/create-comment/create-comment.mjs b/components/airtable_oauth/actions/create-comment/create-comment.mjs index fbfdd1e44ce7c..9c141e33d614f 100644 --- a/components/airtable_oauth/actions/create-comment/create-comment.mjs +++ b/components/airtable_oauth/actions/create-comment/create-comment.mjs @@ -4,7 +4,7 @@ export default { key: "airtable_oauth-create-comment", name: "Create Comment", description: "Create a comment on a selected record. [See the documentation](https://airtable.com/developers/web/api/create-comment)", - version: "0.0.11", + version: "0.0.12", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/create-field/create-field.mjs b/components/airtable_oauth/actions/create-field/create-field.mjs index a1364fab69b7a..84660a7381da7 100644 --- a/components/airtable_oauth/actions/create-field/create-field.mjs +++ b/components/airtable_oauth/actions/create-field/create-field.mjs @@ -5,7 +5,7 @@ export default { key: "airtable_oauth-create-field", name: "Create Field", description: "Create a new field in a table. [See the documentation](https://airtable.com/developers/web/api/create-field)", - version: "0.1.3", + version: "0.1.4", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/create-multiple-records/create-multiple-records.mjs b/components/airtable_oauth/actions/create-multiple-records/create-multiple-records.mjs index 3c3b2fbdc6b64..4154e7c031a17 100644 --- a/components/airtable_oauth/actions/create-multiple-records/create-multiple-records.mjs +++ b/components/airtable_oauth/actions/create-multiple-records/create-multiple-records.mjs @@ -9,7 +9,7 @@ export default { key: "airtable_oauth-create-multiple-records", name: "Create Multiple Records", description: "Create one or more records in a table in a single operation with an array. [See the documentation](https://airtable.com/developers/web/api/create-records)", - version: "0.0.11", + version: "0.0.12", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/create-or-update-record/create-or-update-record.mjs b/components/airtable_oauth/actions/create-or-update-record/create-or-update-record.mjs index 720c563168f5d..1f4578952d1eb 100644 --- a/components/airtable_oauth/actions/create-or-update-record/create-or-update-record.mjs +++ b/components/airtable_oauth/actions/create-or-update-record/create-or-update-record.mjs @@ -6,7 +6,7 @@ export default { key: "airtable_oauth-create-or-update-record", name: "Create or Update Record", description: "Create a new record or update an existing one. [See the documentation](https://airtable.com/developers/web/api/create-records)", - version: "0.1.3", + version: "0.1.4", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/create-single-record/create-single-record.mjs b/components/airtable_oauth/actions/create-single-record/create-single-record.mjs index fb82608de6685..334532f1d8777 100644 --- a/components/airtable_oauth/actions/create-single-record/create-single-record.mjs +++ b/components/airtable_oauth/actions/create-single-record/create-single-record.mjs @@ -6,7 +6,7 @@ export default { key: "airtable_oauth-create-single-record", name: "Create Single Record", description: "Adds a record to a table.", - version: "0.0.12", + version: "0.0.13", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/create-table/create-table.mjs b/components/airtable_oauth/actions/create-table/create-table.mjs index 05eb7d64e0112..cc2cc85d6d526 100644 --- a/components/airtable_oauth/actions/create-table/create-table.mjs +++ b/components/airtable_oauth/actions/create-table/create-table.mjs @@ -4,7 +4,7 @@ export default { key: "airtable_oauth-create-table", name: "Create Table", description: "Create a new table. [See the documentation](https://airtable.com/developers/web/api/create-table)", - version: "0.0.11", + version: "0.0.12", type: "action", props: { airtable, diff --git a/components/airtable_oauth/actions/delete-record/delete-record.mjs b/components/airtable_oauth/actions/delete-record/delete-record.mjs index 15f6dcc5b03a2..0e8f5e5012ccf 100644 --- a/components/airtable_oauth/actions/delete-record/delete-record.mjs +++ b/components/airtable_oauth/actions/delete-record/delete-record.mjs @@ -5,7 +5,7 @@ export default { key: "airtable_oauth-delete-record", name: "Delete Record", description: "Delete a selected record from a table. [See the documentation](https://airtable.com/developers/web/api/delete-record)", - version: "0.0.11", + version: "0.0.12", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/get-record-or-create/get-record-or-create.mjs b/components/airtable_oauth/actions/get-record-or-create/get-record-or-create.mjs index 0f9b92b186293..426606806cd0e 100644 --- a/components/airtable_oauth/actions/get-record-or-create/get-record-or-create.mjs +++ b/components/airtable_oauth/actions/get-record-or-create/get-record-or-create.mjs @@ -6,7 +6,7 @@ export default { key: "airtable_oauth-get-record-or-create", name: "Get Record Or Create", description: "Get a specific record, or create one if it doesn't exist. [See the documentation](https://airtable.com/developers/web/api/create-records)", - version: "0.0.13", + version: "0.0.14", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/get-record/get-record.mjs b/components/airtable_oauth/actions/get-record/get-record.mjs index b7175ee4810c8..162a9488cd214 100644 --- a/components/airtable_oauth/actions/get-record/get-record.mjs +++ b/components/airtable_oauth/actions/get-record/get-record.mjs @@ -6,7 +6,7 @@ export default { key: "airtable_oauth-get-record", name: "Get Record", description: "Get data of a selected record from a table. [See the documentation](https://airtable.com/developers/web/api/get-record)", - version: "0.0.12", + version: "0.0.13", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/list-bases/list-bases.mjs b/components/airtable_oauth/actions/list-bases/list-bases.mjs index d3512389ca7aa..7b1e1f3f9edeb 100644 --- a/components/airtable_oauth/actions/list-bases/list-bases.mjs +++ b/components/airtable_oauth/actions/list-bases/list-bases.mjs @@ -6,7 +6,7 @@ export default { description: "Get the list of bases that can be accessed. [See the documentation](https://airtable.com/developers/web/api/list-bases)", type: "action", - version: "0.0.2", + version: "0.0.3", props: { airtable, }, diff --git a/components/airtable_oauth/actions/list-records-in-view/list-records-in-view.mjs b/components/airtable_oauth/actions/list-records-in-view/list-records-in-view.mjs index f4d24dfc8a616..486a9661f3a71 100644 --- a/components/airtable_oauth/actions/list-records-in-view/list-records-in-view.mjs +++ b/components/airtable_oauth/actions/list-records-in-view/list-records-in-view.mjs @@ -6,7 +6,7 @@ export default { name: "List Records in View", description: "Retrieve records from a view, optionally sorting and filtering results. [See the documentation](https://airtable.com/developers/web/api/list-views)", type: "action", - version: "0.0.11", + version: "0.0.12", ...commonList, props: { accountTierAlert: { diff --git a/components/airtable_oauth/actions/list-records/list-records.mjs b/components/airtable_oauth/actions/list-records/list-records.mjs index 216e49f3943cf..10a44dc438ab5 100644 --- a/components/airtable_oauth/actions/list-records/list-records.mjs +++ b/components/airtable_oauth/actions/list-records/list-records.mjs @@ -6,7 +6,7 @@ export default { name: "List Records", description: "Retrieve records from a table, optionally sorting and filtering results. [See the documentation](https://airtable.com/developers/web/api/list-records)", type: "action", - version: "0.0.11", + version: "0.0.12", ...commonList, props: { ...common.props, diff --git a/components/airtable_oauth/actions/list-tables/list-tables.mjs b/components/airtable_oauth/actions/list-tables/list-tables.mjs index 91732d1f64d9b..41d9c28f5eba0 100644 --- a/components/airtable_oauth/actions/list-tables/list-tables.mjs +++ b/components/airtable_oauth/actions/list-tables/list-tables.mjs @@ -6,7 +6,7 @@ export default { description: "Get a list of tables in the selected base. [See the documentation](https://airtable.com/developers/web/api/get-base-schema)", type: "action", - version: "0.0.2", + version: "0.0.3", props: { airtable, baseId: { diff --git a/components/airtable_oauth/actions/search-records/search-records.mjs b/components/airtable_oauth/actions/search-records/search-records.mjs index ac6fad35d09ed..bb12dbda114c4 100644 --- a/components/airtable_oauth/actions/search-records/search-records.mjs +++ b/components/airtable_oauth/actions/search-records/search-records.mjs @@ -5,7 +5,7 @@ export default { key: "airtable_oauth-search-records", name: "Search Records", description: "Search for a record by formula or by field value. [See the documentation](https://airtable.com/developers/web/api/list-records)", - version: "0.0.13", + version: "0.0.14", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/update-comment/update-comment.mjs b/components/airtable_oauth/actions/update-comment/update-comment.mjs index d0f68946e55be..f118ace0beabe 100644 --- a/components/airtable_oauth/actions/update-comment/update-comment.mjs +++ b/components/airtable_oauth/actions/update-comment/update-comment.mjs @@ -4,7 +4,7 @@ export default { key: "airtable_oauth-update-comment", name: "Update Comment", description: "Update an existing comment on a selected record. [See the documentation](https://airtable.com/developers/web/api/update-comment)", - version: "0.0.11", + version: "0.0.12", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/update-field/update-field.mjs b/components/airtable_oauth/actions/update-field/update-field.mjs index eb1df1eda9998..5d3c2c1f40e5c 100644 --- a/components/airtable_oauth/actions/update-field/update-field.mjs +++ b/components/airtable_oauth/actions/update-field/update-field.mjs @@ -5,7 +5,7 @@ export default { key: "airtable_oauth-update-field", name: "Update Field", description: "Update an existing field in a table. [See the documentation](https://airtable.com/developers/web/api/update-field)", - version: "0.0.11", + version: "0.0.12", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/update-record/update-record.mjs b/components/airtable_oauth/actions/update-record/update-record.mjs index d8719ddcae566..e75a15305690f 100644 --- a/components/airtable_oauth/actions/update-record/update-record.mjs +++ b/components/airtable_oauth/actions/update-record/update-record.mjs @@ -6,7 +6,7 @@ export default { key: "airtable_oauth-update-record", name: "Update Record", description: "Update a single record in a table by Record ID. [See the documentation](https://airtable.com/developers/web/api/update-record)", - version: "0.0.12", + version: "0.0.13", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/actions/update-table/update-table.mjs b/components/airtable_oauth/actions/update-table/update-table.mjs index 2e1dfd6677fcc..745cd97747fd1 100644 --- a/components/airtable_oauth/actions/update-table/update-table.mjs +++ b/components/airtable_oauth/actions/update-table/update-table.mjs @@ -4,7 +4,7 @@ export default { key: "airtable_oauth-update-table", name: "Update Table", description: "Update an existing table. [See the documentation](https://airtable.com/developers/web/api/update-table)", - version: "0.0.11", + version: "0.0.12", type: "action", props: { ...common.props, diff --git a/components/airtable_oauth/package.json b/components/airtable_oauth/package.json index f03f7a931b12d..332f6bd29c03d 100644 --- a/components/airtable_oauth/package.json +++ b/components/airtable_oauth/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/airtable_oauth", - "version": "0.5.1", + "version": "0.5.2", "description": "Pipedream Airtable (OAuth) Components", "main": "airtable_oauth.app.mjs", "keywords": [ diff --git a/components/airtable_oauth/sources/new-field/new-field.mjs b/components/airtable_oauth/sources/new-field/new-field.mjs index eddcfc85faec4..6d24b4354f993 100644 --- a/components/airtable_oauth/sources/new-field/new-field.mjs +++ b/components/airtable_oauth/sources/new-field/new-field.mjs @@ -5,7 +5,7 @@ export default { name: "New Field Created (Instant)", description: "Emit new event when a field is created in the selected table. [See the documentation](https://airtable.com/developers/web/api/get-base-schema)", key: "airtable_oauth-new-field", - version: "1.0.3", + version: "1.0.4", type: "source", dedupe: "unique", methods: { diff --git a/components/airtable_oauth/sources/new-modified-or-deleted-records-instant/new-modified-or-deleted-records-instant.mjs b/components/airtable_oauth/sources/new-modified-or-deleted-records-instant/new-modified-or-deleted-records-instant.mjs index a6c59ad99454e..deb29e5548f73 100644 --- a/components/airtable_oauth/sources/new-modified-or-deleted-records-instant/new-modified-or-deleted-records-instant.mjs +++ b/components/airtable_oauth/sources/new-modified-or-deleted-records-instant/new-modified-or-deleted-records-instant.mjs @@ -8,7 +8,7 @@ export default { name: "New Record Created, Updated or Deleted (Instant)", description: "Emit new event when a record is added, updated, or deleted in a table or selected view.", key: "airtable_oauth-new-modified-or-deleted-records-instant", - version: "0.1.3", + version: "0.1.4", type: "source", dedupe: "unique", props: { diff --git a/components/airtable_oauth/sources/new-modified-or-deleted-records/new-modified-or-deleted-records.mjs b/components/airtable_oauth/sources/new-modified-or-deleted-records/new-modified-or-deleted-records.mjs index 247993857414d..99e2955126f33 100644 --- a/components/airtable_oauth/sources/new-modified-or-deleted-records/new-modified-or-deleted-records.mjs +++ b/components/airtable_oauth/sources/new-modified-or-deleted-records/new-modified-or-deleted-records.mjs @@ -6,7 +6,7 @@ export default { name: "New, Modified or Deleted Records", description: "Emit new event each time a record is added, updated, or deleted in an Airtable table. Supports tables up to 10,000 records", key: "airtable_oauth-new-modified-or-deleted-records", - version: "0.0.12", + version: "0.0.13", type: "source", dedupe: "unique", props: { diff --git a/components/airtable_oauth/sources/new-or-modified-field/new-or-modified-field.mjs b/components/airtable_oauth/sources/new-or-modified-field/new-or-modified-field.mjs index 47c067f500fa1..39cea4ae9404f 100644 --- a/components/airtable_oauth/sources/new-or-modified-field/new-or-modified-field.mjs +++ b/components/airtable_oauth/sources/new-or-modified-field/new-or-modified-field.mjs @@ -6,7 +6,7 @@ export default { name: "New or Modified Field (Instant)", description: "Emit new event when a field is created or updated in the selected table", key: "airtable_oauth-new-or-modified-field", - version: "1.0.3", + version: "1.0.4", type: "source", dedupe: "unique", methods: { diff --git a/components/airtable_oauth/sources/new-or-modified-records-in-view/new-or-modified-records-in-view.mjs b/components/airtable_oauth/sources/new-or-modified-records-in-view/new-or-modified-records-in-view.mjs index c432337448c48..eed1bb8e63514 100644 --- a/components/airtable_oauth/sources/new-or-modified-records-in-view/new-or-modified-records-in-view.mjs +++ b/components/airtable_oauth/sources/new-or-modified-records-in-view/new-or-modified-records-in-view.mjs @@ -6,7 +6,7 @@ export default { name: "New or Modified Records in View", description: "Emit new event for each new or modified record in a view", key: "airtable_oauth-new-or-modified-records-in-view", - version: "0.0.13", + version: "0.0.14", type: "source", props: { ...base.props, diff --git a/components/airtable_oauth/sources/new-or-modified-records/new-or-modified-records.mjs b/components/airtable_oauth/sources/new-or-modified-records/new-or-modified-records.mjs index 1c466d83cd1fc..110dd1a6f5ab4 100644 --- a/components/airtable_oauth/sources/new-or-modified-records/new-or-modified-records.mjs +++ b/components/airtable_oauth/sources/new-or-modified-records/new-or-modified-records.mjs @@ -6,7 +6,7 @@ export default { name: "New or Modified Records (Instant)", key: "airtable_oauth-new-or-modified-records", description: "Emit new event for each new or modified record in a table or view", - version: "1.0.3", + version: "1.0.4", type: "source", dedupe: "unique", methods: { diff --git a/components/airtable_oauth/sources/new-records-in-view/new-records-in-view.mjs b/components/airtable_oauth/sources/new-records-in-view/new-records-in-view.mjs index 85b835af702a5..fe19ece5a4869 100644 --- a/components/airtable_oauth/sources/new-records-in-view/new-records-in-view.mjs +++ b/components/airtable_oauth/sources/new-records-in-view/new-records-in-view.mjs @@ -6,7 +6,7 @@ export default { name: "New Records in View", description: "Emit new event for each new record in a view", key: "airtable_oauth-new-records-in-view", - version: "0.0.12", + version: "0.0.13", type: "source", dedupe: "unique", props: { diff --git a/components/airtable_oauth/sources/new-records/new-records.mjs b/components/airtable_oauth/sources/new-records/new-records.mjs index 117927ef51bbb..21779b96e7273 100644 --- a/components/airtable_oauth/sources/new-records/new-records.mjs +++ b/components/airtable_oauth/sources/new-records/new-records.mjs @@ -5,7 +5,7 @@ export default { name: "New Record(s) Created (Instant)", description: "Emit new event for each new record in a table", key: "airtable_oauth-new-records", - version: "1.0.3", + version: "1.0.4", type: "source", dedupe: "unique", methods: {