@frogbotai/piece-google-sheets 0.29.0 → 0.31.0

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/rows.js CHANGED
@@ -2,20 +2,31 @@ import { createHash } from 'node:crypto';
2
2
  import { parse } from 'csv-parse/sync';
3
3
  import { z } from 'zod';
4
4
  import { defineAction } from './define.js';
5
- import { batch, batchOutput, cells, columnIndex, mapRows, readInput, readRows, readValues, requestOptions, rowNumber, rowOutput, rowValues, sheetInput, sheetRange, updatedRow, updateOutput, valueInputOption, worksheet, } from './shared.js';
6
- const writeInput = sheetInput.extend({ values: rowValues, valueInputOption });
7
- const writeOutput = z.object({ row: rowNumber, updates: updateOutput });
5
+ import { batch, batchOutput, cells, columnIndex, mapRows, readInput, readRows, readValues, requestOptions, rowNumber, rowOutput, rowValues, sheetInput, sheetRange, updatedRow, updateOutput, valueInputOption, worksheet } from './shared.js';
6
+ const writeInput = sheetInput.extend({
7
+ values: rowValues,
8
+ valueInputOption
9
+ });
10
+ const writeOutput = z.object({
11
+ row: rowNumber,
12
+ updates: updateOutput
13
+ });
8
14
  async function appendValues(args) {
9
15
  const sheet = await worksheet(args);
10
16
  const { data } = await args.client.sheets.spreadsheets.values.append({
11
17
  spreadsheetId: args.input.spreadsheetId,
12
18
  range: sheetRange(sheet.title, 'A:A'),
13
19
  valueInputOption: args.input.valueInputOption,
14
- requestBody: { majorDimension: 'ROWS', values: [cells(args.input.values)] },
20
+ requestBody: {
21
+ majorDimension: 'ROWS',
22
+ values: [
23
+ cells(args.input.values)
24
+ ]
25
+ }
15
26
  }, requestOptions(args.req));
16
27
  return {
17
28
  row: updatedRow(data.updates?.updatedRange),
18
- updates: updateOutput.parse(data.updates ?? {}),
29
+ updates: updateOutput.parse(data.updates ?? {})
19
30
  };
20
31
  }
21
32
  export const appendRow = defineAction({
@@ -24,10 +35,10 @@ export const appendRow = defineAction({
24
35
  input: writeInput,
25
36
  output: writeOutput,
26
37
  idempotent: false,
27
- run: appendValues,
38
+ run: appendValues
28
39
  });
29
40
  const insertInput = writeInput.extend({
30
- afterRow: z.number().int().nonnegative().max(9_999_999).default(1),
41
+ afterRow: z.number().int().nonnegative().max(9_999_999).default(1)
31
42
  });
32
43
  export const insertRow = defineAction({
33
44
  slug: 'insertRow',
@@ -35,7 +46,7 @@ export const insertRow = defineAction({
35
46
  input: insertInput,
36
47
  output: writeOutput,
37
48
  idempotent: false,
38
- async run(args) {
49
+ async run (args) {
39
50
  const sheet = await worksheet(args);
40
51
  const row = args.input.afterRow + 1;
41
52
  await batch(args, [
@@ -45,89 +56,119 @@ export const insertRow = defineAction({
45
56
  sheetId: args.input.sheetId,
46
57
  dimension: 'ROWS',
47
58
  startIndex: row - 1,
48
- endIndex: row,
59
+ endIndex: row
49
60
  },
50
- inheritFromBefore: row > 1,
51
- },
52
- },
61
+ inheritFromBefore: row > 1
62
+ }
63
+ }
53
64
  ]);
54
65
  const { data } = await args.client.sheets.spreadsheets.values.update({
55
66
  spreadsheetId: args.input.spreadsheetId,
56
67
  range: sheetRange(sheet.title, `${row}:${row}`),
57
68
  valueInputOption: args.input.valueInputOption,
58
- requestBody: { majorDimension: 'ROWS', values: [cells(args.input.values)] },
69
+ requestBody: {
70
+ majorDimension: 'ROWS',
71
+ values: [
72
+ cells(args.input.values)
73
+ ]
74
+ }
59
75
  }, requestOptions(args.req));
60
- return { row, updates: updateOutput.parse(data) };
61
- },
76
+ return {
77
+ row,
78
+ updates: updateOutput.parse(data)
79
+ };
80
+ }
81
+ });
82
+ const updateInput = writeInput.extend({
83
+ row: rowNumber
62
84
  });
63
- const updateInput = writeInput.extend({ row: rowNumber });
64
85
  export const updateRow = defineAction({
65
86
  slug: 'updateRow',
66
87
  description: 'Update a row; empty and null values leave existing cells unchanged.',
67
88
  input: updateInput,
68
89
  output: writeOutput,
69
90
  idempotent: true,
70
- async run(args) {
91
+ async run (args) {
71
92
  const sheet = await worksheet(args);
72
93
  const { data } = await args.client.sheets.spreadsheets.values.update({
73
94
  spreadsheetId: args.input.spreadsheetId,
74
95
  range: sheetRange(sheet.title, `${args.input.row}:${args.input.row}`),
75
96
  valueInputOption: args.input.valueInputOption,
76
- requestBody: { majorDimension: 'ROWS', values: [cells(args.input.values, true)] },
97
+ requestBody: {
98
+ majorDimension: 'ROWS',
99
+ values: [
100
+ cells(args.input.values, true)
101
+ ]
102
+ }
77
103
  }, requestOptions(args.req));
78
- return { row: args.input.row, updates: updateOutput.parse(data) };
79
- },
104
+ return {
105
+ row: args.input.row,
106
+ updates: updateOutput.parse(data)
107
+ };
108
+ }
80
109
  });
81
110
  const updatesInput = sheetInput.extend({
82
- rows: z.array(z.object({ row: rowNumber.optional(), values: rowValues })),
83
- valueInputOption,
111
+ rows: z.array(z.object({
112
+ row: rowNumber.optional(),
113
+ values: rowValues
114
+ })),
115
+ valueInputOption
84
116
  });
85
- const updatesOutput = z
86
- .object({
117
+ const updatesOutput = z.object({
87
118
  spreadsheetId: z.string().nullish(),
88
119
  totalUpdatedRows: z.number().nullish(),
89
120
  totalUpdatedCells: z.number().nullish(),
90
- responses: z.array(updateOutput).nullish(),
91
- })
92
- .passthrough();
121
+ responses: z.array(updateOutput).nullish()
122
+ }).passthrough();
93
123
  export const updateRows = defineAction({
94
124
  slug: 'updateRows',
95
125
  description: 'Batch-update rows, skipping entries without a row number and leaving empty cells unchanged.',
96
126
  input: updatesInput,
97
127
  output: updatesOutput,
98
128
  idempotent: true,
99
- async run(args) {
100
- const rows = args.input.rows.filter((row) => row.row != null);
101
- if (!rows.length)
102
- return { totalUpdatedRows: 0, totalUpdatedCells: 0, responses: [] };
129
+ async run (args) {
130
+ const rows = args.input.rows.filter((row)=>row.row != null);
131
+ if (!rows.length) return {
132
+ totalUpdatedRows: 0,
133
+ totalUpdatedCells: 0,
134
+ responses: []
135
+ };
103
136
  const sheet = await worksheet(args);
104
137
  const { data } = await args.client.sheets.spreadsheets.values.batchUpdate({
105
138
  spreadsheetId: args.input.spreadsheetId,
106
139
  requestBody: {
107
140
  valueInputOption: args.input.valueInputOption,
108
- data: rows.map((row) => ({
109
- range: sheetRange(sheet.title, `${row.row}:${row.row}`),
110
- majorDimension: 'ROWS',
111
- values: [cells(row.values, true)],
112
- })),
113
- },
141
+ data: rows.map((row)=>({
142
+ range: sheetRange(sheet.title, `${row.row}:${row.row}`),
143
+ majorDimension: 'ROWS',
144
+ values: [
145
+ cells(row.values, true)
146
+ ]
147
+ }))
148
+ }
114
149
  }, requestOptions(args.req));
115
150
  return updatesOutput.parse(data);
116
- },
151
+ }
117
152
  });
118
153
  const appendRowsInput = sheetInput.extend({
119
154
  data: z.discriminatedUnion('format', [
120
- z.object({ format: z.literal('columns'), rows: z.array(rowValues) }),
121
- z.object({ format: z.literal('json'), rows: z.array(z.record(z.string(), z.json())) }),
122
- z.object({ format: z.literal('csv'), text: z.string() }),
155
+ z.object({
156
+ format: z.literal('columns'),
157
+ rows: z.array(rowValues)
158
+ }),
159
+ z.object({
160
+ format: z.literal('json'),
161
+ rows: z.array(z.record(z.string(), z.json()))
162
+ }),
163
+ z.object({
164
+ format: z.literal('csv'),
165
+ text: z.string()
166
+ })
123
167
  ]),
124
168
  headerRow: rowNumber.default(1),
125
169
  overwrite: z.boolean().default(false),
126
- duplicateColumn: z
127
- .string()
128
- .regex(/^[A-Z]{1,3}$/)
129
- .optional(),
130
- valueInputOption,
170
+ duplicateColumn: z.string().regex(/^[A-Z]{1,3}$/).optional(),
171
+ valueInputOption
131
172
  });
132
173
  export const appendRows = defineAction({
133
174
  slug: 'appendRows',
@@ -136,197 +177,227 @@ export const appendRows = defineAction({
136
177
  output: z.object({
137
178
  insertedRows: z.number().int().nonnegative(),
138
179
  updates: updateOutput.or(updatesOutput),
139
- clearedRanges: z.array(z.string()).optional(),
180
+ clearedRanges: z.array(z.string()).optional()
140
181
  }),
141
182
  idempotent: false,
142
- async run(args) {
183
+ async run (args) {
143
184
  const { input, client, req } = args;
144
185
  const sheet = await worksheet(args);
145
186
  const headerRange = sheetRange(sheet.title, `${input.headerRow}:${input.headerRow}`);
146
- const headers = ((await readValues({ ...args, spreadsheetId: input.spreadsheetId, range: headerRange }))[0] ??
147
- []).map(String);
187
+ const headers = ((await readValues({
188
+ ...args,
189
+ spreadsheetId: input.spreadsheetId,
190
+ range: headerRange
191
+ }))[0] ?? []).map(String);
148
192
  let rows;
149
- if (input.data.format === 'columns')
150
- rows = input.data.rows.map((row) => cells(row));
193
+ if (input.data.format === 'columns') rows = input.data.rows.map((row)=>cells(row));
151
194
  else if (input.data.format === 'csv') {
152
- if (!headers.length)
153
- throw new Error('CSV import requires a populated worksheet header row.');
195
+ if (!headers.length) throw new Error('CSV import requires a populated worksheet header row.');
154
196
  const records = parse(input.data.text, {
155
197
  columns: true,
156
198
  bom: true,
157
- skip_empty_lines: true,
199
+ skip_empty_lines: true
158
200
  });
159
- rows = records.map((record) => {
160
- const normalized = Object.fromEntries(Object.entries(record).map(([key, value]) => [key.trim().toLowerCase(), value]));
161
- return headers.map((header) => normalized[header.trim().toLowerCase()] ?? '');
201
+ rows = records.map((record)=>{
202
+ const normalized = Object.fromEntries(Object.entries(record).map(([key, value])=>[
203
+ key.trim().toLowerCase(),
204
+ value
205
+ ]));
206
+ return headers.map((header)=>normalized[header.trim().toLowerCase()] ?? '');
162
207
  });
163
- }
164
- else {
208
+ } else {
165
209
  const records = input.data.rows;
166
- const additions = [...new Set(records.flatMap(Object.keys))].filter((key) => !headers.includes(key));
210
+ const additions = [
211
+ ...new Set(records.flatMap(Object.keys))
212
+ ].filter((key)=>!headers.includes(key));
167
213
  headers.push(...additions);
168
214
  if (additions.length) {
169
215
  await client.sheets.spreadsheets.values.update({
170
216
  spreadsheetId: input.spreadsheetId,
171
217
  range: headerRange,
172
218
  valueInputOption: 'USER_ENTERED',
173
- requestBody: { values: [headers] },
219
+ requestBody: {
220
+ values: [
221
+ headers
222
+ ]
223
+ }
174
224
  }, requestOptions(req));
175
225
  }
176
- rows = records.map((record) => cells(headers.map((header) => record[header] ?? '')));
226
+ rows = records.map((record)=>cells(headers.map((header)=>record[header] ?? '')));
177
227
  }
178
228
  if (input.overwrite) {
179
229
  const start = input.headerRow + 1;
180
- const updates = rows.length
181
- ? updatesOutput.parse((await client.sheets.spreadsheets.values.batchUpdate({
182
- spreadsheetId: input.spreadsheetId,
183
- requestBody: {
184
- valueInputOption: input.valueInputOption,
185
- data: [
186
- {
187
- range: sheetRange(sheet.title, `A${start}`),
188
- majorDimension: 'ROWS',
189
- values: rows,
190
- },
191
- ],
192
- },
193
- }, requestOptions(req))).data)
194
- : { totalUpdatedRows: 0 };
230
+ const updates = rows.length ? updatesOutput.parse((await client.sheets.spreadsheets.values.batchUpdate({
231
+ spreadsheetId: input.spreadsheetId,
232
+ requestBody: {
233
+ valueInputOption: input.valueInputOption,
234
+ data: [
235
+ {
236
+ range: sheetRange(sheet.title, `A${start}`),
237
+ majorDimension: 'ROWS',
238
+ values: rows
239
+ }
240
+ ]
241
+ }
242
+ }, requestOptions(req))).data) : {
243
+ totalUpdatedRows: 0
244
+ };
195
245
  const end = sheet.gridProperties?.rowCount ?? start + rows.length;
196
- const clearedRanges = start + rows.length <= end
197
- ? ((await client.sheets.spreadsheets.values.batchClear({
198
- spreadsheetId: input.spreadsheetId,
199
- requestBody: {
200
- ranges: [sheetRange(sheet.title, `${start + rows.length}:${end}`)],
201
- },
202
- }, requestOptions(req))).data.clearedRanges ?? [])
203
- : [];
204
- return { insertedRows: rows.length, updates, clearedRanges };
246
+ const clearedRanges = start + rows.length <= end ? (await client.sheets.spreadsheets.values.batchClear({
247
+ spreadsheetId: input.spreadsheetId,
248
+ requestBody: {
249
+ ranges: [
250
+ sheetRange(sheet.title, `${start + rows.length}:${end}`)
251
+ ]
252
+ }
253
+ }, requestOptions(req))).data.clearedRanges ?? [] : [];
254
+ return {
255
+ insertedRows: rows.length,
256
+ updates,
257
+ clearedRanges
258
+ };
205
259
  }
206
260
  if (input.duplicateColumn) {
207
261
  const index = columnIndex(input.duplicateColumn);
208
262
  const existing = await readValues({
209
263
  ...args,
210
264
  spreadsheetId: input.spreadsheetId,
211
- range: sheetRange(sheet.title),
265
+ range: sheetRange(sheet.title)
212
266
  });
213
- const keys = new Set(existing
214
- .filter((row) => index < Math.max(headers.length, row.length))
215
- .map((row) => String(row[index] ?? '')
216
- .trim()
217
- .toLowerCase()));
218
- rows = rows.filter((row, rowIndex) => {
267
+ const keys = new Set(existing.filter((row)=>index < Math.max(headers.length, row.length)).map((row)=>String(row[index] ?? '').trim().toLowerCase()));
268
+ rows = rows.filter((row, rowIndex)=>{
219
269
  const original = input.data.format === 'columns' ? input.data.rows[rowIndex] : undefined;
220
- const value = original
221
- ? Array.isArray(original)
222
- ? original[index]
223
- : original[input.duplicateColumn]
224
- : row[index];
270
+ const value = original ? Array.isArray(original) ? original[index] : original[input.duplicateColumn] : row[index];
225
271
  return value == null || !keys.has(String(value).trim().toLowerCase());
226
272
  });
227
273
  }
228
- if (!rows.length)
229
- return { insertedRows: 0, updates: {} };
274
+ if (!rows.length) return {
275
+ insertedRows: 0,
276
+ updates: {}
277
+ };
230
278
  const { data } = await client.sheets.spreadsheets.values.append({
231
279
  spreadsheetId: input.spreadsheetId,
232
280
  range: sheetRange(sheet.title, 'A:A'),
233
281
  valueInputOption: input.valueInputOption,
234
- requestBody: { majorDimension: 'ROWS', values: rows },
282
+ requestBody: {
283
+ majorDimension: 'ROWS',
284
+ values: rows
285
+ }
235
286
  }, requestOptions(req));
236
- return { insertedRows: rows.length, updates: updateOutput.parse(data.updates ?? {}) };
237
- },
287
+ return {
288
+ insertedRows: rows.length,
289
+ updates: updateOutput.parse(data.updates ?? {})
290
+ };
291
+ }
292
+ });
293
+ const deleteInput = sheetInput.extend({
294
+ row: rowNumber
238
295
  });
239
- const deleteInput = sheetInput.extend({ row: rowNumber });
240
296
  export const deleteRow = defineAction({
241
297
  slug: 'deleteRow',
242
298
  description: 'Delete a row and shift following rows upward.',
243
299
  input: deleteInput,
244
300
  output: batchOutput,
245
301
  idempotent: false,
246
- run: async (args) => batchOutput.parse(await batch(args, [
247
- {
248
- deleteDimension: {
249
- range: {
250
- sheetId: args.input.sheetId,
251
- dimension: 'ROWS',
252
- startIndex: args.input.row - 1,
253
- endIndex: args.input.row,
254
- },
255
- },
256
- },
257
- ])),
302
+ run: async (args)=>batchOutput.parse(await batch(args, [
303
+ {
304
+ deleteDimension: {
305
+ range: {
306
+ sheetId: args.input.sheetId,
307
+ dimension: 'ROWS',
308
+ startIndex: args.input.row - 1,
309
+ endIndex: args.input.row
310
+ }
311
+ }
312
+ }
313
+ ]))
258
314
  });
259
315
  const rowSelection = z.discriminatedUnion('mode', [
260
- z
261
- .object({ mode: z.literal('range'), startRow: rowNumber, endRow: rowNumber.optional() })
262
- .refine((value) => value.endRow == null || value.endRow >= value.startRow, 'End row must not precede start row.'),
263
- z.object({ mode: z.literal('list'), rows: z.array(rowNumber).min(1) }),
316
+ z.object({
317
+ mode: z.literal('range'),
318
+ startRow: rowNumber,
319
+ endRow: rowNumber.optional()
320
+ }).refine((value)=>value.endRow == null || value.endRow >= value.startRow, 'End row must not precede start row.'),
321
+ z.object({
322
+ mode: z.literal('list'),
323
+ rows: z.array(rowNumber).min(1)
324
+ })
264
325
  ]);
265
- const deleteRowsInput = sheetInput.extend({ selection: rowSelection });
326
+ const deleteRowsInput = sheetInput.extend({
327
+ selection: rowSelection
328
+ });
266
329
  export const deleteRows = defineAction({
267
330
  slug: 'deleteRows',
268
331
  description: 'Delete a row range or distinct row numbers in descending order.',
269
332
  input: deleteRowsInput,
270
333
  output: z.object({
271
- deletedRanges: z.array(z.object({ startRow: rowNumber, endRow: rowNumber })),
272
- result: batchOutput,
334
+ deletedRanges: z.array(z.object({
335
+ startRow: rowNumber,
336
+ endRow: rowNumber
337
+ })),
338
+ result: batchOutput
273
339
  }),
274
340
  idempotent: false,
275
- async run(args) {
341
+ async run (args) {
276
342
  const selection = args.input.selection;
277
- const ranges = selection.mode === 'range'
278
- ? [{ startRow: selection.startRow, endRow: selection.endRow ?? selection.startRow }]
279
- : [...new Set(selection.rows)]
280
- .sort((a, b) => b - a)
281
- .map((row) => ({ startRow: row, endRow: row }));
343
+ const ranges = selection.mode === 'range' ? [
344
+ {
345
+ startRow: selection.startRow,
346
+ endRow: selection.endRow ?? selection.startRow
347
+ }
348
+ ] : [
349
+ ...new Set(selection.rows)
350
+ ].sort((a, b)=>b - a).map((row)=>({
351
+ startRow: row,
352
+ endRow: row
353
+ }));
282
354
  return {
283
355
  deletedRanges: ranges,
284
- result: batchOutput.parse(await batch(args, ranges.map(({ startRow, endRow }) => ({
285
- deleteDimension: {
286
- range: {
287
- sheetId: args.input.sheetId,
288
- dimension: 'ROWS',
289
- startIndex: startRow - 1,
290
- endIndex: endRow,
291
- },
292
- },
293
- })))),
356
+ result: batchOutput.parse(await batch(args, ranges.map(({ startRow, endRow })=>({
357
+ deleteDimension: {
358
+ range: {
359
+ sheetId: args.input.sheetId,
360
+ dimension: 'ROWS',
361
+ startIndex: startRow - 1,
362
+ endIndex: endRow
363
+ }
364
+ }
365
+ }))))
294
366
  };
295
- },
367
+ }
296
368
  });
297
369
  const searchInput = readInput.extend({
298
- column: z
299
- .string()
300
- .regex(/^[A-Z]{1,3}$/)
301
- .default('A'),
370
+ column: z.string().regex(/^[A-Z]{1,3}$/).default('A'),
302
371
  searchValue: z.string().default(''),
303
372
  exactMatch: z.boolean().default(false),
304
373
  startRow: rowNumber.default(1),
305
- limit: rowNumber.default(1),
374
+ limit: rowNumber.default(1)
306
375
  });
307
376
  async function searchRows(args) {
308
- const rows = await readRows({ ...args, input: { ...args.input, useHeaderNames: false } }, args.input.startRow);
377
+ const rows = await readRows({
378
+ ...args,
379
+ input: {
380
+ ...args.input,
381
+ useHeaderNames: false
382
+ }
383
+ }, args.input.startRow);
309
384
  const { input } = args;
310
- const matches = rows
311
- .filter(({ values }) => input.searchValue === '' ||
312
- (input.exactMatch
313
- ? values[input.column] === input.searchValue
314
- : values[input.column]?.toLowerCase().includes(input.searchValue.toLowerCase())))
315
- .slice(0, input.limit);
316
- if (!input.useHeaderNames || !matches.length)
317
- return matches;
385
+ const matches = rows.filter(({ values })=>input.searchValue === '' || (input.exactMatch ? values[input.column] === input.searchValue : values[input.column]?.toLowerCase().includes(input.searchValue.toLowerCase()))).slice(0, input.limit);
386
+ if (!input.useHeaderNames || !matches.length) return matches;
318
387
  const sheet = await worksheet(args);
319
388
  const headers = (await readValues({
320
389
  ...args,
321
390
  spreadsheetId: input.spreadsheetId,
322
- range: sheetRange(sheet.title, `${input.headerRow}:${input.headerRow}`),
391
+ range: sheetRange(sheet.title, `${input.headerRow}:${input.headerRow}`)
323
392
  }))[0] ?? [];
324
- return matches.map((row) => mapRows({
325
- values: [Object.values(row.values)],
326
- startRow: row.row,
327
- headers,
328
- useHeaderNames: true,
329
- })[0]);
393
+ return matches.map((row)=>mapRows({
394
+ values: [
395
+ Object.values(row.values)
396
+ ],
397
+ startRow: row.row,
398
+ headers,
399
+ useHeaderNames: true
400
+ })[0]);
330
401
  }
331
402
  export const findRows = defineAction({
332
403
  slug: 'findRows',
@@ -334,70 +405,101 @@ export const findRows = defineAction({
334
405
  input: searchInput,
335
406
  output: z.array(rowOutput),
336
407
  idempotent: true,
337
- run: searchRows,
408
+ run: searchRows
409
+ });
410
+ const findOrCreateInput = searchInput.extend({
411
+ values: rowValues,
412
+ valueInputOption
338
413
  });
339
- const findOrCreateInput = searchInput.extend({ values: rowValues, valueInputOption });
340
414
  export const findOrCreateRow = defineAction({
341
415
  slug: 'findOrCreateRow',
342
416
  description: 'Return the first matching row, or append supplied values when no row matches.',
343
417
  input: findOrCreateInput,
344
- output: rowOutput.extend({ found: z.boolean(), created: z.boolean() }),
418
+ output: rowOutput.extend({
419
+ found: z.boolean(),
420
+ created: z.boolean()
421
+ }),
345
422
  idempotent: false,
346
- async run(args) {
347
- const [found] = await searchRows({ ...args, input: { ...args.input, limit: 1 } });
348
- if (found)
349
- return { ...found, found: true, created: false };
423
+ async run (args) {
424
+ const [found] = await searchRows({
425
+ ...args,
426
+ input: {
427
+ ...args.input,
428
+ limit: 1
429
+ }
430
+ });
431
+ if (found) return {
432
+ ...found,
433
+ found: true,
434
+ created: false
435
+ };
350
436
  const result = await appendValues(args);
351
437
  let headers = [];
352
438
  if (args.input.useHeaderNames) {
353
439
  const sheet = await worksheet(args);
354
- headers =
355
- (await readValues({
356
- ...args,
357
- spreadsheetId: args.input.spreadsheetId,
358
- range: sheetRange(sheet.title, `${args.input.headerRow}:${args.input.headerRow}`),
359
- }))[0] ?? [];
440
+ headers = (await readValues({
441
+ ...args,
442
+ spreadsheetId: args.input.spreadsheetId,
443
+ range: sheetRange(sheet.title, `${args.input.headerRow}:${args.input.headerRow}`)
444
+ }))[0] ?? [];
360
445
  }
361
446
  return {
362
447
  ...mapRows({
363
- values: [cells(args.input.values)],
448
+ values: [
449
+ cells(args.input.values)
450
+ ],
364
451
  startRow: result.row,
365
452
  headers,
366
- useHeaderNames: args.input.useHeaderNames,
453
+ useHeaderNames: args.input.useHeaderNames
367
454
  })[0],
368
455
  found: false,
369
- created: true,
456
+ created: true
370
457
  };
371
- },
458
+ }
459
+ });
460
+ const getRowInput = readInput.extend({
461
+ row: rowNumber
372
462
  });
373
- const getRowInput = readInput.extend({ row: rowNumber });
374
463
  export const getRow = defineAction({
375
464
  slug: 'getRow',
376
465
  description: 'Read one physical row, returning found=false when it is beyond the populated range.',
377
466
  input: getRowInput,
378
467
  output: z.union([
379
- rowOutput.extend({ found: z.literal(true) }),
380
- z.object({ found: z.literal(false), row: z.null() }),
468
+ rowOutput.extend({
469
+ found: z.literal(true)
470
+ }),
471
+ z.object({
472
+ found: z.literal(false),
473
+ row: z.null()
474
+ })
381
475
  ]),
382
476
  idempotent: true,
383
- async run(args) {
477
+ async run (args) {
384
478
  const [row] = await readRows(args, args.input.row, args.input.row);
385
- return row ? { found: true, ...row } : { found: false, row: null };
386
- },
479
+ return row ? {
480
+ found: true,
481
+ ...row
482
+ } : {
483
+ found: false,
484
+ row: null
485
+ };
486
+ }
487
+ });
488
+ const getRowsInput = readInput.extend({
489
+ skipHeaders: z.boolean().default(false)
387
490
  });
388
- const getRowsInput = readInput.extend({ skipHeaders: z.boolean().default(false) });
389
491
  export const getRows = defineAction({
390
492
  slug: 'getRows',
391
493
  description: 'Read all populated rows, optionally skipping the header and preceding rows.',
392
494
  input: getRowsInput,
393
495
  output: z.array(rowOutput),
394
496
  idempotent: true,
395
- run: (args) => readRows(args, args.input.skipHeaders ? args.input.headerRow + 1 : 1),
497
+ run: (args)=>readRows(args, args.input.skipHeaders ? args.input.headerRow + 1 : 1)
396
498
  });
397
499
  const nextInput = readInput.extend({
398
500
  startRow: rowNumber.default(1),
399
501
  batchSize: rowNumber.default(1),
400
- memoryKey: z.string().min(1).default('row_number'),
502
+ memoryKey: z.string().min(1).default('row_number')
401
503
  });
402
504
  export const getNextRows = defineAction({
403
505
  slug: 'getNextRows',
@@ -405,50 +507,63 @@ export const getNextRows = defineAction({
405
507
  input: nextInput,
406
508
  output: z.array(rowOutput),
407
509
  idempotent: false,
408
- async run(args) {
510
+ async run (args) {
409
511
  const { req, input } = args;
410
512
  requestOptions(req);
411
- const owner = req.user ? [req.user.collection, req.user.id] : ['developer'];
513
+ const owner = req.user ? [
514
+ req.user.collection,
515
+ req.user.id
516
+ ] : [
517
+ 'developer'
518
+ ];
412
519
  if (req.user && (!req.user.collection || req.user.id == null)) {
413
520
  throw new Error('A persistent Sheets cursor requires an identifiable owner.');
414
521
  }
415
522
  let instanceSlug;
416
- for (const instance of req.frogbot.config.pieces.instances) {
417
- if (instance.piece !== 'google-sheets')
418
- continue;
419
- const client = await instance.client({ req }).catch(() => undefined);
523
+ for (const instance of req.frogbot.config.pieces.instances){
524
+ if (instance.piece !== 'google-sheets') continue;
525
+ const client = await instance.client({
526
+ req
527
+ }).catch(()=>undefined);
420
528
  if (client === args.client) {
421
529
  instanceSlug = instance.slug;
422
530
  break;
423
531
  }
424
532
  }
425
533
  if (!instanceSlug) {
426
- throw new Error('Register the Google Sheets piece instance in your configuration before using a persistent cursor.');
534
+ throw new Error("Use this Google Sheets instance in an agent's tools or triggers, or in `connections`, before using a persistent cursor.");
427
535
  }
428
- const key = `pieces:google-sheets:cursor:${createHash('sha256')
429
- .update(JSON.stringify([owner, instanceSlug, input.spreadsheetId, input.sheetId, input.memoryKey]))
430
- .digest('hex')}`;
431
- return req.frogbot.kv.lock(`${key}:lock`, 30_000, async ({ signal: lease }) => {
432
- const signal = req.signal ? AbortSignal.any([req.signal, lease]) : lease;
536
+ const key = `pieces:google-sheets:cursor:${createHash('sha256').update(JSON.stringify([
537
+ owner,
538
+ instanceSlug,
539
+ input.spreadsheetId,
540
+ input.sheetId,
541
+ input.memoryKey
542
+ ])).digest('hex')}`;
543
+ return req.frogbot.kv.lock(`${key}:lock`, 30_000, async ({ signal: lease })=>{
544
+ const signal = req.signal ? AbortSignal.any([
545
+ req.signal,
546
+ lease
547
+ ]) : lease;
433
548
  signal.throwIfAborted();
434
549
  const stored = await req.frogbot.kv.get(key);
435
550
  const start = stored == null ? input.startRow : z.number().int().positive().max(10_000_001).parse(stored);
436
551
  const lockedArgs = {
437
552
  ...args,
438
553
  req: new Proxy(req, {
439
- get: (target, property) => property === 'signal' ? signal : Reflect.get(target, property),
440
- }),
554
+ get: (target, property)=>property === 'signal' ? signal : Reflect.get(target, property)
555
+ })
441
556
  };
442
557
  let rows = await readRows(lockedArgs, start, start + input.batchSize - 1);
443
558
  if (rows.length < input.batchSize) {
444
559
  rows = (await readRows(lockedArgs, start)).slice(0, input.batchSize);
445
560
  }
446
561
  signal.throwIfAborted();
447
- if (rows.length)
448
- await req.frogbot.kv.set(key, start + rows.length);
562
+ if (rows.length) await req.frogbot.kv.set(key, start + rows.length);
449
563
  signal.throwIfAborted();
450
564
  return rows;
451
565
  });
452
- },
566
+ }
453
567
  });
568
+
454
569
  //# sourceMappingURL=rows.js.map