@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/api.d.ts.map +1 -1
- package/dist/api.js +92 -58
- package/dist/api.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +14 -3
- package/dist/client.js.map +1 -1
- package/dist/config.js +10 -3
- package/dist/config.js.map +1 -1
- package/dist/define.js +1 -0
- package/dist/define.js.map +1 -1
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +48 -34
- package/dist/files.js.map +1 -1
- package/dist/index.js +15 -11
- package/dist/index.js.map +1 -1
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +320 -205
- package/dist/rows.js.map +1 -1
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +81 -62
- package/dist/shared.js.map +1 -1
- package/dist/worksheets.d.ts.map +1 -1
- package/dist/worksheets.js +220 -128
- package/dist/worksheets.js.map +1 -1
- package/package.json +5 -5
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
|
|
6
|
-
const writeInput = sheetInput.extend({
|
|
7
|
-
|
|
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: {
|
|
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: {
|
|
69
|
+
requestBody: {
|
|
70
|
+
majorDimension: 'ROWS',
|
|
71
|
+
values: [
|
|
72
|
+
cells(args.input.values)
|
|
73
|
+
]
|
|
74
|
+
}
|
|
59
75
|
}, requestOptions(args.req));
|
|
60
|
-
return {
|
|
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: {
|
|
97
|
+
requestBody: {
|
|
98
|
+
majorDimension: 'ROWS',
|
|
99
|
+
values: [
|
|
100
|
+
cells(args.input.values, true)
|
|
101
|
+
]
|
|
102
|
+
}
|
|
77
103
|
}, requestOptions(args.req));
|
|
78
|
-
return {
|
|
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({
|
|
83
|
-
|
|
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)
|
|
101
|
-
if (!rows.length)
|
|
102
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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({
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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({
|
|
147
|
-
|
|
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])
|
|
161
|
-
|
|
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 = [
|
|
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: {
|
|
219
|
+
requestBody: {
|
|
220
|
+
values: [
|
|
221
|
+
headers
|
|
222
|
+
]
|
|
223
|
+
}
|
|
174
224
|
}, requestOptions(req));
|
|
175
225
|
}
|
|
176
|
-
rows = records.map((record)
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
{
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return {
|
|
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
|
-
|
|
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
|
-
|
|
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: {
|
|
282
|
+
requestBody: {
|
|
283
|
+
majorDimension: 'ROWS',
|
|
284
|
+
values: rows
|
|
285
|
+
}
|
|
235
286
|
}, requestOptions(req));
|
|
236
|
-
return {
|
|
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)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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({
|
|
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({
|
|
272
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
.
|
|
281
|
-
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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({
|
|
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
|
-
|
|
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)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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({
|
|
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({
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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: [
|
|
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({
|
|
380
|
-
|
|
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 ? {
|
|
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)
|
|
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 ? [
|
|
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
|
-
|
|
419
|
-
|
|
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(
|
|
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
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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)
|
|
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
|