@mcpher/gas-fakes 1.0.18 → 1.0.20

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.
Files changed (80) hide show
  1. package/.aiexclude +6 -0
  2. package/README.md +17 -23
  3. package/fakeadvdocuments.js +0 -0
  4. package/gasmess/bruce/pbx.js +109 -8
  5. package/gasmess/bruce/sorting.js +106 -0
  6. package/gprompts/gas-inventory.json +80 -0
  7. package/gprompts/gas-inventory.mjs +80 -0
  8. package/gprompts/inventory-list.json +1 -0
  9. package/gprompts/model.json +34 -0
  10. package/gprompts/package-lock.json +869 -0
  11. package/gprompts/package.json +17 -0
  12. package/gprompts/regenerate-progress-reports.sh +38 -0
  13. package/gprompts/temp_fetch.mjs +9 -0
  14. package/gprompts/update-progress.js +142 -0
  15. package/package.json +6 -1
  16. package/setup.sh +147 -0
  17. package/src/index.js +10 -2
  18. package/src/services/advforms/app.js +31 -0
  19. package/src/services/advforms/fakeadvforms.js +221 -0
  20. package/src/services/advforms/fakeadvformsform.js +80 -0
  21. package/src/services/advforms/formsapis.js +15 -0
  22. package/src/services/advgmail/app.js +31 -0
  23. package/src/services/advgmail/fakeadvgmail.js +39 -0
  24. package/src/services/advgmail/fakeadvgmaillabels.js +119 -0
  25. package/src/services/advgmail/fakeadvgmailusers.js +23 -0
  26. package/src/services/advgmail/gmailapis.js +15 -0
  27. package/src/services/documentapp/appenderhelpers.js +53 -25
  28. package/src/services/documentapp/elementblasters.js +78 -2
  29. package/src/services/documentapp/elementhelpers.js +105 -1
  30. package/src/services/documentapp/elementoptions.js +120 -49
  31. package/src/services/documentapp/fakebody.js +244 -1
  32. package/src/services/documentapp/fakedocument.js +55 -4
  33. package/src/services/documentapp/fakedocumentapp.js +14 -5
  34. package/src/services/documentapp/fakeelement.js +20 -3
  35. package/src/services/documentapp/fakelistitem.js +244 -24
  36. package/src/services/documentapp/fakeparagraph.js +287 -62
  37. package/src/services/documentapp/faketable.js +16 -0
  38. package/src/services/documentapp/faketablerow.js +15 -0
  39. package/src/services/documentapp/nrhelpers.js +1 -0
  40. package/src/services/documentapp/shadowdocument.js +37 -33
  41. package/src/services/documentapp/stylehelpers.js +1 -0
  42. package/src/services/enums/docsenums.js +2 -1
  43. package/src/services/enums/formsenums.js +62 -0
  44. package/src/services/enums/gmailenums.js +8 -0
  45. package/src/services/formapp/app.js +44 -0
  46. package/src/services/formapp/fakecheckboxitem.js +142 -0
  47. package/src/services/formapp/fakechoice.js +45 -0
  48. package/src/services/formapp/fakeform.js +167 -0
  49. package/src/services/formapp/fakeformapp.js +107 -0
  50. package/src/services/formapp/fakeformitem.js +218 -0
  51. package/src/services/formapp/formitemregistry.js +23 -0
  52. package/src/services/formapp/formitems.js +6 -0
  53. package/src/services/gmailapp/app.js +44 -0
  54. package/src/services/gmailapp/fakegmailapp.js +35 -0
  55. package/src/services/gmailapp/fakegmaillabel.js +44 -0
  56. package/src/services/scriptapp/app.js +2 -1
  57. package/src/services/scriptapp/behavior.js +2 -1
  58. package/src/services/slidesapp/app.js +3 -3
  59. package/src/services/spreadsheetapp/fakeprotection.js +38 -38
  60. package/src/services/spreadsheetapp/fakesheet.js +2 -4
  61. package/src/services/spreadsheetapp/fakesheetrange.js +0 -1
  62. package/src/support/formscacher.js +7 -0
  63. package/src/support/gmailcacher.js +7 -0
  64. package/src/support/helpers.js +3 -5
  65. package/src/support/proxies.js +4 -1
  66. package/src/support/sxdocs.js +3 -3
  67. package/src/support/sxforms.js +50 -0
  68. package/src/support/sxgmail.js +55 -0
  69. package/src/support/syncit.js +7 -0
  70. package/src/support/utils.js +46 -15
  71. package/src/support/workersync/sxfunctions.js +5 -9
  72. package/togas.bash +18 -5
  73. package/ghissues/image-size-inconsistency-issue.sh +0 -46
  74. package/ghissues/issue-positioned-image.sh +0 -25
  75. package/ghissues/post-issue.sh +0 -53
  76. package/ghissues/review-sandbox-listing-issue.sh +0 -45
  77. package/ghissues/sandbox-issue.sh +0 -31
  78. package/ghissues/setup-under-construction.sh +0 -107
  79. /package/src/services/{session → base}/app.js +0 -0
  80. /package/src/services/{session → base}/fakesession.js +0 -0
package/.aiexclude ADDED
@@ -0,0 +1,6 @@
1
+ testongas
2
+ gaslibtests
3
+ gasmess
4
+ node_modules
5
+ shells
6
+ README.RU.md
package/README.md CHANGED
@@ -33,12 +33,24 @@ In order to duplicate the OAuth management handled by GAS, we'll use Application
33
33
 
34
34
  #### Application default credentials
35
35
 
36
- At the very least you need to add the gcp project id you'll be using for testing, plus the id of some file you have access to - this'll be used to check that you have set up ADC properly.
36
+ In order to avoid a bunch of Node specific code and credentials, yet still handle OAuth, I figured that we could simply rely on ADC. This is a problem I already wrote about here [Application Default Credentials with Google Cloud and Workspace APIs](https://ramblings.mcpher.com/application-default-credentials-with-google-cloud-and-workspace-apis/)
37
+
38
+ At the very least you need to add the gcp project id and optionally the id of some file you have access to - this'll be used to check that you have set up ADC properly.
39
+
40
+ #### Option 1 - automated setup
41
+
42
+ In the shells folder run
43
+ ```sh
44
+ bash setup.sh
45
+ ```
46
+ This will prompt you for project id and a file id to test with. It will also ask it you want to set up all the parameters needed for testing. If you answer yes, this will set up other things from the .env-template you can ignore unless you're planning to run the test suite. More information on that is in [collaborators info](collaborators.md)
47
+
48
+ Running set up will enhance your .env file and auth you in ADC with the required scopes
37
49
 
38
- There are other things in the .env-template you can ignore unless you're planning to run the test suite. More information on that is in [collaborators info](collaborators.md)
50
+ #### Option 2 - manual setup
39
51
 
52
+ You can setup the .env file your self using the .env.template as a guide.
40
53
 
41
- These should be in your .env file to enable ADC authentication. The purpose of the DRIVE_TEST_FILE_ID is so that the script can check you've enabled ADC correctly by pinging a file you have access to. The GCP_PROJECT_ID is required as it will be used by gas-fakes to access the workspace apis on your behalf.
42
54
  ```
43
55
  # must set these
44
56
  GCP_PROJECT_ID="add your gcp project id here"
@@ -55,25 +67,6 @@ EXTRA_SCOPES=",https://www.googleapis.com/auth/drive,https://www.googleapis.com/
55
67
 
56
68
  - goto ./shells and execute sp.sh
57
69
 
58
- ### OAuth
59
-
60
- There's 2 pieces to this solution.
61
-
62
- #### Application default credentials (ADC)
63
-
64
- In order to avoid a bunch of Node specific code and credentials, yet still handle OAuth, I figured that we could simply rely on ADC. This is a problem I already wrote about here [Application Default Credentials with Google Cloud and Workspace APIs](https://ramblings.mcpher.com/application-default-credentials-with-google-cloud-and-workspace-apis/)
65
-
66
- This section in your env file controls which scopes you plan to use.
67
-
68
- ```
69
- we'll use the default config for application default credentials
70
- AC=default
71
- # these are the scopes set by default - take some of these out if you want to minimize access
72
- DEFAULT_SCOPES="https://www.googleapis.com/auth/userinfo.email,openid,https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/sqlservice.login"
73
- EXTRA_SCOPES=",https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/spreadsheets"
74
-
75
- .....etc
76
- ```
77
70
 
78
71
  #### Manifest file
79
72
 
@@ -144,4 +137,5 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
144
137
  - [setup env](setup-env.md) - ([credit Eric Shapiro] - additional info on contents of .env file
145
138
  - [this file](README.md)
146
139
  - [named colors](named-colors.md)
147
- - [sandbox](sandbox.md)
140
+ - [sandbox](sandbox.md)
141
+ - [named range identity](named-range-identity.md)
File without changes
@@ -2,7 +2,108 @@ import '../../main.js';
2
2
  import { moveToTempFolder, deleteTempFile } from '../tempfolder.js';
3
3
  import { report, scl } from './dreport.js';
4
4
 
5
+
6
+
5
7
  const suffix = "-bruce"
8
+ const tli = () => {
9
+ let doc = DocumentApp.create("abc")
10
+ const id = doc.getId()
11
+ moveToTempFolder(id, suffix)
12
+ let body = doc.getBody();
13
+ let li = body.appendListItem("Initial list item text.");
14
+
15
+ doc = scl(doc)
16
+ let d = Docs.Documents.get(id)
17
+ console.log('after append item', doc.getBody().getNumChildren())
18
+ console.log(JSON.stringify(d.body.content))
19
+
20
+ body = doc.getBody()
21
+ li = body.getChild(1)
22
+ li.clear();
23
+
24
+ doc = scl(doc)
25
+ d = Docs.Documents.get(id)
26
+ console.log('after clear', doc.getBody().getNumChildren())
27
+ console.log(JSON.stringify(d.body.content))
28
+
29
+ body = doc.getBody()
30
+ li = body.getChild(1)
31
+ li.setText("New text after clear.");
32
+ doc = scl(doc)
33
+ d = Docs.Documents.get(id)
34
+ console.log('after new text', doc.getBody().getNumChildren())
35
+ console.log(JSON.stringify(d.body.content))
36
+
37
+ body = doc.getBody()
38
+ li = body.getChild(1)
39
+ li.setText("Image test: "); // setText returns void, so we can't chain.
40
+
41
+ doc = scl(doc)
42
+ d = Docs.Documents.get(id)
43
+ console.log('after image text', doc.getBody().getNumChildren())
44
+ console.log(JSON.stringify(d.body.content))
45
+
46
+
47
+ const imageUrl = 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png';
48
+ const imageBlob = UrlFetchApp.fetch(imageUrl).getBlob();
49
+ body = doc.getBody()
50
+ li = body.getChild(1)
51
+ li.appendInlineImage(imageBlob.copyBlob());
52
+
53
+ console.log('after inlinr image', doc.getBody().getNumChildren())
54
+ console.log(JSON.stringify(d.body.content))
55
+
56
+ deleteTempFile(id)
57
+
58
+ }
59
+ tli()
60
+
61
+ const tx2 = () => {
62
+ let doc = DocumentApp.create("abc")
63
+ const id = doc.getId()
64
+ moveToTempFolder(id, suffix)
65
+
66
+ let body = doc.getBody()
67
+
68
+ const p1 = body.appendParagraph("p1")
69
+ doc = scl(doc)
70
+ let d = Docs.Documents.get(id)
71
+ console.log(JSON.stringify(d))
72
+
73
+ const attributesToSet = {
74
+ [DocumentApp.Attribute.HORIZONTAL_ALIGNMENT]: DocumentApp.HorizontalAlignment.CENTER,
75
+ [DocumentApp.Attribute.ITALIC]: true,
76
+ [DocumentApp.Attribute.FONT_FAMILY]: 'Comic Sans MS'
77
+ };
78
+
79
+ body = doc.getBody()
80
+ body.setAttributes(attributesToSet);
81
+ doc = scl(doc)
82
+ d = Docs.Documents.get(id)
83
+ console.log(JSON.stringify(d))
84
+
85
+
86
+ deleteTempFile(id)
87
+ }
88
+
89
+ const tx1 = () => {
90
+ let doc = DocumentApp.create("abc")
91
+ const id = doc.getId()
92
+ moveToTempFolder(id, suffix)
93
+
94
+
95
+
96
+ const body = doc.getBody();
97
+ const paraText = "p1";
98
+
99
+ const appendedPara = body.appendParagraph(paraText);
100
+ const paraToTest = body.getChild(1);
101
+ doc = scl(doc)
102
+ console.log(report(Docs.Documents.get(id, { includeTabsContent: true }), `\n1.empty document`))
103
+ const attributes = paraToTest.getAttributes();
104
+
105
+ deleteTempFile(id)
106
+ }
6
107
 
7
108
  const tabsa = () => {
8
109
 
@@ -11,11 +112,11 @@ const tabsa = () => {
11
112
  moveToTempFolder(id, suffix)
12
113
 
13
114
  let body = doc.getBody()
14
- console.log (body)
115
+ console.log(body)
15
116
  doc = scl(doc)
16
- console.log(report(Docs.Documents.get(id, {includeTabsContent: true}), `\n1.empty document`))
117
+ console.log(report(Docs.Documents.get(id, { includeTabsContent: true }), `\n1.empty document`))
118
+
17
119
 
18
-
19
120
  deleteTempFile(id)
20
121
  }
21
122
 
@@ -70,7 +171,7 @@ const pbnew = () => {
70
171
  console.log(report(Docs.Documents.get(id), `\n4.inserted para 2`))
71
172
 
72
173
  body = doc.getBody()
73
- body.insertTable(1, [['eboo', 'fbar']] )
174
+ body.insertTable(1, [['eboo', 'fbar']])
74
175
  doc = scl(doc)
75
176
  console.log(report(Docs.Documents.get(id), `\nt4.inserted table at child 1`))
76
177
 
@@ -81,12 +182,12 @@ const pbnew = () => {
81
182
 
82
183
  body = doc.getBody()
83
184
  let c = body.getChild(2)
84
- c.appendText( 'p2 appended text')
185
+ c.appendText('p2 appended text')
85
186
  doc = scl(doc)
86
187
  console.log(report(Docs.Documents.get(id), `\n6.appended text to para 2`))
87
188
 
88
189
  body = doc.getBody()
89
- body.insertTable(3, [['bar', 'foo'], ['barfoo', 'foobar'], ['eboo', 'fbar']] )
190
+ body.insertTable(3, [['bar', 'foo'], ['barfoo', 'foobar'], ['eboo', 'fbar']])
90
191
  doc = scl(doc)
91
192
  console.log(report(Docs.Documents.get(id), `\nt6.inserted table at child 3`))
92
193
 
@@ -107,7 +208,7 @@ const pbnew = () => {
107
208
  console.log(report(Docs.Documents.get(id), `\n9.inserted page break in para 3`))
108
209
 
109
210
  body = doc.getBody()
110
- c=body.getChild(3)
211
+ c = body.getChild(3)
111
212
  c.appendText('after pagebreak in 3')
112
213
  doc = scl(doc)
113
214
  console.log(report(Docs.Documents.get(id), `\n10.inserted text after pagebreak in 3`))
@@ -120,7 +221,7 @@ const pbnew = () => {
120
221
  deleteTempFile(id)
121
222
  }
122
223
 
123
- pbnew()
224
+
124
225
 
125
226
 
126
227
 
@@ -0,0 +1,106 @@
1
+
2
+ /**
3
+ * Compares two values using Google Sheets' ascending sort logic.
4
+ * @param {*} a first value to compare
5
+ * @param {*} b second value to compare
6
+ * @returns {number} a<b: -1, a===b: 0, a>b: 1
7
+ */
8
+ const compareMixedValues = (a, b) => {
9
+ const isBlankA = (a === null || a === undefined || a === "");
10
+ const isBlankB = (b === null || b === undefined || b === "");
11
+
12
+ // Handle blanks - always sorted to the top in this ascending comparator
13
+ if (isBlankA && !isBlankB) return -1;
14
+ if (!isBlankA && isBlankB) return 1;
15
+ if (isBlankA && isBlankB) return 0;
16
+
17
+ // Get type priorities (lower number = sorts first)
18
+ const getTypePriority = (val) => {
19
+ // Correct Google Sheets ascending priority
20
+ if (typeof val === 'number') return 1;
21
+ if (typeof val === 'string') return 2;
22
+ if (typeof val === 'boolean') return 3;
23
+ if (val instanceof Date) return 4;
24
+ return 5;
25
+ };
26
+
27
+ const priorityA = getTypePriority(a);
28
+ const priorityB = getTypePriority(b);
29
+
30
+ // If different types, sort by type priority
31
+ if (priorityA !== priorityB) {
32
+ return priorityA < priorityB ? -1 : 1;
33
+ }
34
+
35
+ // Same types - compare values
36
+ if (typeof a === 'number' && typeof b === 'number') {
37
+ return a - b;
38
+ }
39
+ if (typeof a === 'string' && typeof b === 'string') {
40
+ return a.localeCompare(b, undefined, { sensitivity: 'base', numeric: true });
41
+ }
42
+ if (typeof a === 'boolean' && typeof b === 'boolean') {
43
+ return (a === b) ? 0 : (a ? 1 : -1);
44
+ }
45
+ if (a instanceof Date && b instanceof Date) {
46
+ return a.getTime() - b.getTime();
47
+ }
48
+
49
+ // Fallback for other types
50
+ const strA = String(a);
51
+ const strB = String(b);
52
+ return strA.localeCompare(strB, undefined, { sensitivity: 'base' });
53
+ };
54
+ // ... your sort2d function remains correct, just pass the 'ascending' flag to compareMixedValues.
55
+ export const sort2d = (spec, arr) => {
56
+ const deepCopy = arr.map(row => [...row]);
57
+ return deepCopy.sort((a, b) => {
58
+ for (const s of spec) {
59
+ const index = (typeof s === 'object' && s !== null) ? s.column - 1 : s - 1;
60
+ const ascending = (typeof s === 'object' && s !== null) ? s.ascending !== false : true;
61
+ let result = compareMixedValues(a[index], b[index]);
62
+ if (!ascending) {
63
+ result = -result;
64
+ }
65
+ if (result !== 0) {
66
+ return result;
67
+ }
68
+ }
69
+ return 0;
70
+ });
71
+ };
72
+
73
+ // Test function to verify behavior
74
+ export const testSorting = (spec) => {
75
+ const testData = [
76
+ [false, 120, 'cheese', 'peach', 'armpit', 10],
77
+ ['kiwi', 'orange', 'apple', 'bub ble', 'bucket', 'armpit'],
78
+ ['plum', 'butter', 'buckle', 'grape', 65, 65],
79
+ [false, "", 'armpit', 'orange', 21, 'buckle'],
80
+ ['cherry', 'foo', 'butter', 21, 'red eye', 'foo'],
81
+ ['kiwi', 77, false, 'banana', 'red eye', 'apple'],
82
+ ['buckle', 'bar', 'foo', 21, 10, 'foo'],
83
+ ['bub ble', 65, 'butter', 'buckle', 'bub ble', null],
84
+ ['cheese', 'pear', 'pear', 77, 10, 'foo'],
85
+ [
86
+ 'bub ble',
87
+ 'armpit',
88
+ 'melon',
89
+ 'buckle',
90
+ 'red eye',
91
+ 3.141592653589793
92
+ ],
93
+ ['pear', false, 0.3963452962629548, 'foo', 'banana', 'plum'],
94
+ ['cherry', true, 'apple', 'cheese', 'butter', 'peach']
95
+ ]
96
+
97
+ console.log('Original:', testData);
98
+
99
+ // Sort by column 2 (booleans) ascending
100
+ const sorted = sort2d(spec, testData);
101
+ console.log('Sorted by column 2 (booleans ASC):', sorted);
102
+
103
+ return sorted;
104
+ };
105
+ const spec = [1,{column:2 , ascending:false}]
106
+ testSorting(spec)
@@ -0,0 +1,80 @@
1
+ {
2
+ "Base Service": {
3
+ "name": "Base Service",
4
+ "link": "https://developers.google.com/apps-script/reference/base",
5
+ "classes": {
6
+ "Classes": {
7
+ "methods": {},
8
+ "properties": {}
9
+ },
10
+ "Blob": {
11
+ "methods": {},
12
+ "properties": {}
13
+ },
14
+ "BlobSource": {
15
+ "methods": {},
16
+ "properties": {}
17
+ },
18
+ "Browser": {
19
+ "methods": {},
20
+ "properties": {}
21
+ },
22
+ "Button": {
23
+ "methods": {},
24
+ "properties": {}
25
+ },
26
+ "ButtonSet": {
27
+ "methods": {},
28
+ "properties": {}
29
+ },
30
+ "ColorType": {
31
+ "methods": {},
32
+ "properties": {}
33
+ },
34
+ "Logger": {
35
+ "methods": {},
36
+ "properties": {}
37
+ },
38
+ "Menu": {
39
+ "methods": {},
40
+ "properties": {}
41
+ },
42
+ "MimeType": {
43
+ "methods": {},
44
+ "properties": {}
45
+ },
46
+ "Month": {
47
+ "methods": {},
48
+ "properties": {}
49
+ },
50
+ "PromptResponse": {
51
+ "methods": {},
52
+ "properties": {}
53
+ },
54
+ "RgbColor": {
55
+ "methods": {},
56
+ "properties": {}
57
+ },
58
+ "Session": {
59
+ "methods": {},
60
+ "properties": {}
61
+ },
62
+ "Ui": {
63
+ "methods": {},
64
+ "properties": {}
65
+ },
66
+ "User": {
67
+ "methods": {},
68
+ "properties": {}
69
+ },
70
+ "Weekday": {
71
+ "methods": {},
72
+ "properties": {}
73
+ },
74
+ "console": {
75
+ "methods": {},
76
+ "properties": {}
77
+ }
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,80 @@
1
+ import got from 'got';
2
+ import * as cheerio from 'cheerio';
3
+ import fs from 'fs/promises';
4
+ import { URL } from 'url';
5
+
6
+ const baseUrl = "https://developers.google.com/apps-script/reference/"
7
+ const items = JSON.parse(await fs.readFile('/home/bruce/gas-fakes/gprompts/inventory-list.json', 'utf-8'));
8
+ const outputFile = '/home/bruce/gas-fakes/gprompts/gas-inventory.json';
9
+
10
+ const visited = new Set();
11
+ const queue = items.map (f=>baseUrl+f);
12
+
13
+ async function scrape() {
14
+ const inventory = {};
15
+
16
+ while (queue.length > 0) {
17
+ const url = queue.shift();
18
+ if (visited.has(url)) {
19
+ continue;
20
+ }
21
+ visited.add(url);
22
+
23
+ console.log(`Scraping ${url}`);
24
+ const response = await got(url);
25
+ const $ = cheerio.load(response.body);
26
+
27
+ const serviceName = $('h1').text().split('\n').map(f=>f.replace(/^\s+/,'').trim()).filter (f=>f)[0];
28
+ console.log ('...starting service name',serviceName)
29
+ inventory[serviceName] = {
30
+ name: serviceName,
31
+ link: url,
32
+ classes: {},
33
+ };
34
+
35
+ $('a').each((i, el) => {
36
+ const href = $(el).attr('href');
37
+ if (href && href.startsWith(url)) {
38
+ const absoluteUrl = new URL(href, url).href;
39
+ if (!visited.has(absoluteUrl)) {
40
+ queue.push(absoluteUrl);
41
+ }
42
+ }
43
+ });
44
+
45
+ // Extract classes and methods
46
+ $('h2').each((i, el) => {
47
+ const className = $(el).text().trim();
48
+ if (className) {
49
+ console.log ('...found class',className,'for service',serviceName)
50
+ inventory[serviceName].classes[className] = {
51
+ methods: {},
52
+ properties: {},
53
+ };
54
+
55
+ // Find the table of methods for the current class
56
+ const methodsTable = $(el).nextAll('table').first();
57
+ methodsTable.find('tbody tr').each((j, row) => {
58
+ const methodName = $(row).find('td').eq(1).text().trim();
59
+ const methodLink = $(row).find('td').eq(1).find('a').attr('href');
60
+ const returnType = $(row).find('td').eq(0).text().trim();
61
+
62
+ if (methodName) {
63
+ inventory[serviceName].classes[className].methods[methodName] = {
64
+ link: methodLink ? new URL(methodLink, url).href : '',
65
+ returns: {
66
+ type: returnType,
67
+ link: '' // to be extracted later
68
+ }
69
+ };
70
+ }
71
+ });
72
+ }
73
+ });
74
+ }
75
+
76
+ await fs.writeFile(outputFile, JSON.stringify(inventory, null, 2));
77
+ console.log(`Inventory saved to ${outputFile}`);
78
+ }
79
+
80
+ scrape().catch(console.error);
@@ -0,0 +1 @@
1
+ ["base"]
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "Document Service",
3
+ "link": "https://developers.google.com/apps-script/reference/document",
4
+ "classes": {
5
+ "DocumentApp": {
6
+ "methods": {
7
+ "create": {
8
+ "link": "https://developers.google.com/apps-script/reference/document/document-app#create(String)",
9
+ "returns": {
10
+ "type": "Document",
11
+ "link": "https://developers.google.com/apps-script/reference/document/document"
12
+ }
13
+ },
14
+ "properties": {
15
+ "Attribute": {
16
+ "type": "Attribute",
17
+ "link": "https://developers.google.com/apps-script/reference/document/attribute"
18
+ }
19
+ }
20
+ },
21
+ "Bookmark": {
22
+ "methods": {
23
+ "getId": {
24
+ "link": "https://developers.google.com/apps-script/reference/document/bookmark#getId()",
25
+ "returns": {
26
+ "type": "String",
27
+ "link": "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String"
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }