@mcpher/gas-fakes 1.0.18 → 1.0.19

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 (47) hide show
  1. package/.aiexclude +6 -0
  2. package/fakeadvdocuments.js +0 -0
  3. package/gasmess/bruce/pbx.js +58 -8
  4. package/gasmess/bruce/sorting.js +106 -0
  5. package/ghissues/issue-formapp-create-title-inconsistency.sh +51 -0
  6. package/ghissues/protection-editors-issue.sh +33 -0
  7. package/package.json +5 -2
  8. package/regenerate-progress-reports.sh +38 -0
  9. package/src/index.js +3 -1
  10. package/src/services/advforms/app.js +31 -0
  11. package/src/services/advforms/fakeadvforms.js +221 -0
  12. package/src/services/advforms/fakeadvformsform.js +80 -0
  13. package/src/services/advforms/formsapis.js +15 -0
  14. package/src/services/documentapp/appenderhelpers.js +32 -2
  15. package/src/services/documentapp/elementblasters.js +78 -2
  16. package/src/services/documentapp/elementhelpers.js +106 -1
  17. package/src/services/documentapp/elementoptions.js +98 -17
  18. package/src/services/documentapp/fakebody.js +244 -1
  19. package/src/services/documentapp/fakedocument.js +55 -4
  20. package/src/services/documentapp/fakedocumentapp.js +14 -5
  21. package/src/services/documentapp/fakelistitem.js +72 -1
  22. package/src/services/documentapp/fakeparagraph.js +101 -63
  23. package/src/services/documentapp/shadowdocument.js +27 -33
  24. package/src/services/documentapp/stylehelpers.js +1 -0
  25. package/src/services/enums/docsenums.js +2 -1
  26. package/src/services/enums/formsenums.js +62 -0
  27. package/src/services/formapp/app.js +44 -0
  28. package/src/services/formapp/fakecheckboxitem.js +142 -0
  29. package/src/services/formapp/fakechoice.js +45 -0
  30. package/src/services/formapp/fakeform.js +167 -0
  31. package/src/services/formapp/fakeformapp.js +107 -0
  32. package/src/services/formapp/fakeformitem.js +218 -0
  33. package/src/services/formapp/formitemregistry.js +23 -0
  34. package/src/services/formapp/formitems.js +6 -0
  35. package/src/services/scriptapp/app.js +2 -1
  36. package/src/services/scriptapp/behavior.js +2 -1
  37. package/src/services/spreadsheetapp/fakeprotection.js +39 -38
  38. package/src/services/spreadsheetapp/fakesheet.js +2 -4
  39. package/src/services/spreadsheetapp/fakesheetrange.js +0 -1
  40. package/src/support/formscacher.js +7 -0
  41. package/src/support/helpers.js +1 -3
  42. package/src/support/sxdocs.js +3 -3
  43. package/src/support/sxforms.js +50 -0
  44. package/src/support/syncit.js +4 -0
  45. package/src/support/workersync/sxfunctions.js +1 -0
  46. /package/src/services/{session → base}/app.js +0 -0
  47. /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
File without changes
@@ -2,8 +2,58 @@ 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"
6
8
 
9
+ const tx2 = () => {
10
+ let doc = DocumentApp.create("abc")
11
+ const id = doc.getId()
12
+ moveToTempFolder(id, suffix)
13
+
14
+ let body = doc.getBody()
15
+
16
+ const p1 = body.appendParagraph("p1")
17
+ doc = scl(doc)
18
+ let d = Docs.Documents.get(id)
19
+ console.log(JSON.stringify(d))
20
+
21
+ const attributesToSet = {
22
+ [DocumentApp.Attribute.HORIZONTAL_ALIGNMENT]: DocumentApp.HorizontalAlignment.CENTER,
23
+ [DocumentApp.Attribute.ITALIC]: true,
24
+ [DocumentApp.Attribute.FONT_FAMILY]: 'Comic Sans MS'
25
+ };
26
+
27
+ body = doc.getBody()
28
+ body.setAttributes(attributesToSet);
29
+ doc = scl(doc)
30
+ d = Docs.Documents.get(id)
31
+ console.log(JSON.stringify(d))
32
+
33
+
34
+ deleteTempFile(id)
35
+ }
36
+
37
+ tx2()
38
+ const tx1 = () => {
39
+ let doc = DocumentApp.create("abc")
40
+ const id = doc.getId()
41
+ moveToTempFolder(id, suffix)
42
+
43
+
44
+
45
+ const body = doc.getBody();
46
+ const paraText = "p1";
47
+
48
+ const appendedPara = body.appendParagraph(paraText);
49
+ const paraToTest = body.getChild(1);
50
+ doc = scl(doc)
51
+ console.log(report(Docs.Documents.get(id, { includeTabsContent: true }), `\n1.empty document`))
52
+ const attributes = paraToTest.getAttributes();
53
+
54
+ deleteTempFile(id)
55
+ }
56
+ tx1()
7
57
  const tabsa = () => {
8
58
 
9
59
  let doc = DocumentApp.create("abc")
@@ -11,11 +61,11 @@ const tabsa = () => {
11
61
  moveToTempFolder(id, suffix)
12
62
 
13
63
  let body = doc.getBody()
14
- console.log (body)
64
+ console.log(body)
15
65
  doc = scl(doc)
16
- console.log(report(Docs.Documents.get(id, {includeTabsContent: true}), `\n1.empty document`))
66
+ console.log(report(Docs.Documents.get(id, { includeTabsContent: true }), `\n1.empty document`))
67
+
17
68
 
18
-
19
69
  deleteTempFile(id)
20
70
  }
21
71
 
@@ -70,7 +120,7 @@ const pbnew = () => {
70
120
  console.log(report(Docs.Documents.get(id), `\n4.inserted para 2`))
71
121
 
72
122
  body = doc.getBody()
73
- body.insertTable(1, [['eboo', 'fbar']] )
123
+ body.insertTable(1, [['eboo', 'fbar']])
74
124
  doc = scl(doc)
75
125
  console.log(report(Docs.Documents.get(id), `\nt4.inserted table at child 1`))
76
126
 
@@ -81,12 +131,12 @@ const pbnew = () => {
81
131
 
82
132
  body = doc.getBody()
83
133
  let c = body.getChild(2)
84
- c.appendText( 'p2 appended text')
134
+ c.appendText('p2 appended text')
85
135
  doc = scl(doc)
86
136
  console.log(report(Docs.Documents.get(id), `\n6.appended text to para 2`))
87
137
 
88
138
  body = doc.getBody()
89
- body.insertTable(3, [['bar', 'foo'], ['barfoo', 'foobar'], ['eboo', 'fbar']] )
139
+ body.insertTable(3, [['bar', 'foo'], ['barfoo', 'foobar'], ['eboo', 'fbar']])
90
140
  doc = scl(doc)
91
141
  console.log(report(Docs.Documents.get(id), `\nt6.inserted table at child 3`))
92
142
 
@@ -107,7 +157,7 @@ const pbnew = () => {
107
157
  console.log(report(Docs.Documents.get(id), `\n9.inserted page break in para 3`))
108
158
 
109
159
  body = doc.getBody()
110
- c=body.getChild(3)
160
+ c = body.getChild(3)
111
161
  c.appendText('after pagebreak in 3')
112
162
  doc = scl(doc)
113
163
  console.log(report(Docs.Documents.get(id), `\n10.inserted text after pagebreak in 3`))
@@ -120,7 +170,7 @@ const pbnew = () => {
120
170
  deleteTempFile(id)
121
171
  }
122
172
 
123
- pbnew()
173
+
124
174
 
125
175
 
126
176
 
@@ -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,51 @@
1
+ #!/bin/bash
2
+
3
+ # A shell script to create a GitHub issue for the FormApp.create() title inconsistency.
4
+
5
+ # Issue title
6
+ TITLE="Discrepancy between FormApp.create(title) and Forms API behavior regarding form title and file name"
7
+
8
+ # Issue body in markdown
9
+ BODY=$(cat <<'EOF'
10
+ ### Summary
11
+
12
+ There is a significant and counter-intuitive discrepancy between the documented behavior of `FormApp.create(title)` in Google Apps Script and the constraints of the underlying Google Forms API. `FormApp.create(title)` sets the Google Drive *file name* to the provided `title`, but leaves the form's internal title (retrieved by `form.getTitle()`) blank.
13
+
14
+ This makes it impossible to accurately emulate the Apps Script behavior using the public Forms API, as the API requires a non-empty title for both creation and updates.
15
+
16
+ ### Live Apps Script Behavior
17
+
18
+ When the following Apps Script code is executed:
19
+
20
+ ```javascript
21
+ function testFormCreation() {
22
+ const formName = "foo-form";
23
+ const form = FormApp.create(formName);
24
+ const id = form.getId();
25
+
26
+ console.log('Internal form title:', form.getTitle()); // Logs: "" (an empty string)
27
+
28
+ const file = DriveApp.getFileById(id);
29
+ console.log('Drive file name:', file.getName()); // Logs: "foo-form"
30
+
31
+ // cleanup
32
+ file.setTrashed(true);
33
+ }
34
+ ```
35
+
36
+ The output demonstrates that `FormApp.create()` uses the argument to set the Drive file name, but the form's own title property remains empty.
37
+
38
+ ### The Problem with API Emulation
39
+
40
+ This behavior is problematic to replicate with the v1 Google Forms API because the `forms.create` method requires a non-empty `info.title`. Attempting to then `batchUpdate` the title to an empty string also fails, as the API rejects an empty title on update.
41
+
42
+ ### Conclusion
43
+
44
+ This disconnect means it's impossible for developers using the public Forms API to programmatically create a form that exactly matches the state of one created by `FormApp.create()`. This poses a significant challenge for testing and emulation frameworks.
45
+
46
+ The expected behavior would be for `FormApp.create(title)` to set both the Drive file name and the internal form title, or for the API to allow setting the title to an empty string via an update.
47
+ EOF
48
+ )
49
+
50
+ # Create the issue using GitHub CLI
51
+ gh issue create --title "$TITLE" --body "$BODY" --label "bug" --label "emulation-accuracy" --label "form-app"
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+
3
+ # A shell script to create a GitHub issue for the Protection.getEditors() behavior.
4
+
5
+ # Issue title
6
+ TITLE="Align Protection.getEditors() behavior with Live Apps Script"
7
+
8
+ # Issue body in markdown
9
+ BODY=$(cat <<'EOF'
10
+ ## Summary
11
+
12
+ There is a discrepancy between the live Google Apps Script environment and the `gas-fakes` emulation regarding the return value of `Protection.getEditors()` on a newly created protection.
13
+
14
+ - **Live Apps Script:** `protection.getEditors().length` returns `0`.
15
+ - **`gas-fakes` (current):** `protection.getEditors().length` incorrectly returns `1`.
16
+
17
+ ## Live Behavior Details
18
+
19
+ In the live environment, the owner of a spreadsheet can always edit a protected range or sheet (`protection.canEdit()` returns `true`). However, the owner is **not** automatically included in the explicit list of editors returned by `getEditors()`. This is confirmed by live testing.
20
+
21
+ ## `gas-fakes` Behavior
22
+
23
+ The `gas-fakes` implementation in `fakeprotection.js` was incorrectly adding the current user (as the owner) to the list of editors upon creation of a new `Protection` object.
24
+
25
+ ## TODO
26
+
27
+ 1. Modify `fakeprotection.js` to no longer automatically add the owner to the `editors` list when a new protection is created. The `canEdit()` method should still return `true` for the owner.
28
+ 2. Run the full test suite to ensure this change doesn't have unintended side effects and that the `Protection` tests now pass in both environments.
29
+ EOF
30
+ )
31
+
32
+ # Create the issue using GitHub CLI
33
+ gh issue create --title "$TITLE" --body "$BODY" --label "bug" --label "emulation-accuracy" --label "spreadsheet-app"
package/package.json CHANGED
@@ -42,6 +42,8 @@
42
42
  "testsheetsdata": "cp mainlocal.js main.js && node --env-file=.env ./test/testsheetsdata.js execute",
43
43
  "testdocsadv": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsadv.js execute",
44
44
  "testslidesadv": "cp mainlocal.js main.js && node --env-file=.env ./test/testslidesadv.js execute",
45
+ "testform": "cp mainlocal.js main.js && node --env-file=.env ./test/testform.js execute",
46
+ "testformsadv": "cp mainlocal.js main.js && node --env-file=.env ./test/testformsadv.js execute",
45
47
  "testdocs": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocs.js execute",
46
48
  "testslides": "cp mainlocal.js main.js && node --env-file=.env ./test/testslides.js execute",
47
49
  "testdocsnext": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsnext.js execute",
@@ -53,14 +55,15 @@
53
55
  "testdocsfooters": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsfooters.js execute",
54
56
  "testdocsfootnotes": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsfootnotes.js execute",
55
57
  "testdocsimages": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsimages.js execute",
58
+ "testdocsstyles": "cp mainlocal.js main.js && node --env-file=.env ./test/testdocsstyles.js execute",
56
59
  "testsandbox": "cp mainlocal.js main.js && node --env-file=.env ./test/testsandbox.js execute",
57
60
  "pub": "cp mainlocal.js main.js && npm publish --access public"
58
61
  },
59
62
  "name": "@mcpher/gas-fakes",
60
- "version": "1.0.18",
63
+ "version": "1.0.19",
61
64
  "license": "MIT",
62
65
  "main": "main.js",
63
66
  "description": "A proof of concept implementation of Apps Script Environment on Node",
64
67
  "repository": "github:brucemcpherson/gas-fakes",
65
68
  "homepage": "https://ramblings.mcpher.com/a-proof-of-concept-implementation-of-apps-script-environment-on-node/"
66
- }
69
+ }
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+
3
+
4
+ # This script contains the prompts to regenerate the progress reports for all services.
5
+ # Execute this script and paste the output to the Gemini agent.
6
+
7
+ echo "Generating progress report for Docs service..."
8
+ echo "now do https://developers.google.com/apps-script/reference/document into ./progress/Docs.MD"
9
+
10
+ echo "Generating progress report for Drive service..."
11
+ echo "now do https://developers.google.com/apps-script/reference/drive into ./progress/drive.MD"
12
+
13
+ echo "Generating progress report for Forms service..."
14
+ echo "now do https://developers.google.com/apps-script/reference/forms into ./progress/forms.MD"
15
+
16
+ echo "Generating progress report for Slides service..."
17
+ echo "now do https://developers.google.com/apps-script/reference/slides into ./progress/slides.MD"
18
+
19
+ echo "Generating progress report for Spreadsheet service..."
20
+ echo "now do https://developers.google.com/apps-script/reference/spreadsheet into ./progress/sheets.MD"
21
+
22
+ echo "Generating progress report for Utilities service..."
23
+ echo "now do https://developers.google.com/apps-script/reference/utilities into ./progress/utilities.MD"
24
+
25
+ echo "Generating progress report for UrlFetch service..."
26
+ echo "now do https://developers.google.com/apps-script/reference/url-fetch into ./progress/urlFetch.MD"
27
+
28
+ echo "Generating progress report for Cache service..."
29
+ echo "now do https://developers.google.com/apps-script/reference/cache into ./progress/cache.MD"
30
+
31
+ echo "Generating progress report for Properties service..."
32
+ echo "now do https://developers.google.com/apps-script/reference/properties into ./progress/properties.MD"
33
+
34
+ echo "Generating progress report for Script service..."
35
+ echo "now do https://developers.google.com/apps-script/reference/script into ./progress/script.MD"
36
+
37
+ echo "Generating progress report for Base service..."
38
+ echo "now do https://developers.google.com/apps-script/reference/base into ./progress/base.MD"
package/src/index.js CHANGED
@@ -4,10 +4,12 @@ import './services/urlfetchapp/app.js'
4
4
  import './services/utilities/app.js'
5
5
  import './services/spreadsheetapp/app.js'
6
6
  import './services/stores/app.js'
7
- import './services/session/app.js'
7
+ import './services/base/app.js'
8
8
  import './services/advdrive/app.js'
9
9
  import './services/advsheets/app.js'
10
10
  import './services/advdocs/app.js'
11
11
  import './services/advslides/app.js'
12
12
  import './services/documentapp/app.js'
13
+ import './services/advforms/app.js'
14
+ import './services/formapp/app.js'
13
15
  import './services/slidesapp/app.js'
@@ -0,0 +1,31 @@
1
+
2
+ /**
3
+ * NOTE - Although Apps Script doesnt yet have a Forms advanced service
4
+ * we're going to funnel everything through here as if there was one.
5
+ *
6
+ */
7
+ import { newFakeAdvForms } from './fakeadvforms.js'
8
+ import { Proxies } from '../../support/proxies.js'
9
+
10
+ // This will eventually hold a proxy for Formsapp
11
+ let _app = null
12
+
13
+ /**
14
+ * adds to global space to mimic Apps Script behavior
15
+ */
16
+ const name = "Forms"
17
+ if (typeof globalThis[name] === typeof undefined) {
18
+
19
+ const getApp = () => {
20
+ // if it hasne been intialized yet then do that
21
+ if (!_app) {
22
+ console.log('...activating proxy for', name)
23
+ _app = newFakeAdvForms()
24
+ }
25
+ // this is the actual formsapp we'll return from the proxy
26
+ return _app
27
+ }
28
+
29
+ Proxies.registerProxy(name, getApp)
30
+
31
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Advanced Forms service
3
+ */
4
+ import { Proxies } from '../../support/proxies.js';
5
+ import { advClassMaker } from '../../support/helpers.js';
6
+ import { formsCacher } from '../../support/formscacher.js';
7
+ import { newFakeAdvFormsForm } from './fakeadvformsform.js';
8
+
9
+ class FakeAdvForms {
10
+ constructor() {
11
+ this.__fakeObjectType = "Forms"
12
+
13
+
14
+ const propLists = {
15
+ // Top-level batch update request
16
+ "newBatchUpdateFormRequest": [
17
+ "includeFormInResponse",
18
+ "requests",
19
+ "writeControl"
20
+ ],
21
+ // Individual request types that can be part of a batch update
22
+ "newCreateItemRequest": [
23
+ "item",
24
+ "location"
25
+ ],
26
+ "newDeleteItemRequest": [
27
+ "location"
28
+ ],
29
+ "newMoveItemRequest": [
30
+ "newLocation",
31
+ "originalLocation"
32
+ ],
33
+ "newUpdateFormInfoRequest": [
34
+ "info",
35
+ "updateMask"
36
+ ],
37
+ "newUpdateItemRequest": [
38
+ "item",
39
+ "location",
40
+ "updateMask"
41
+ ],
42
+ "newFormInfo": [
43
+ "title",
44
+ "documentTitle",
45
+ "description"
46
+ ],
47
+ "newUpdateSettingsRequest": [
48
+ "settings",
49
+ "updateMask"
50
+ ],
51
+ "newFormSettings": [
52
+ "emailCollectionType",
53
+ "quizSettings"
54
+ ],
55
+ "newQuizSettings" : [
56
+ "isQuiz"
57
+ ],
58
+ "newItem": [
59
+ "description",
60
+ "imageItem",
61
+ "item",
62
+ "itemId",
63
+ "pageBreakItem",
64
+ "questionGroupItem",
65
+ "questionItem",
66
+ "textItem",
67
+ "title",
68
+ "videoItem"
69
+ ],
70
+ "newQuestionItem": [
71
+ "question",
72
+ "image"
73
+ ],
74
+ "newQuestion": [
75
+ "choiceQuestion",
76
+ "dateQuestion",
77
+ "fileUploadQuestion",
78
+ "grading",
79
+ "questionId",
80
+ "ratingQuestion",
81
+ "required",
82
+ "rowQuestion",
83
+ "scaleQuestion",
84
+ "textQuestion",
85
+ "timeQuestion"
86
+ ],
87
+ "newLocation": [
88
+ "index"
89
+ ],
90
+ "newWriteControl": [
91
+ "requiredRevisionId",
92
+ "targetRevisionId"
93
+ ],
94
+ // The 'Request' schema itself, which is a oneOf type for batch updates
95
+ "newRequest": [
96
+ "createItem",
97
+ "deleteItem",
98
+ "moveItem",
99
+ "updateFormInfo",
100
+ "updateItem",
101
+ "updateSettings"
102
+ ],
103
+ // Further nested items that are builders
104
+ "newImageItem": [
105
+ "image"
106
+ ],
107
+ "newPageBreakItem": [], // No direct properties to set
108
+ "newQuestionGroupItem": [
109
+ "grid",
110
+ "image",
111
+ "questions"
112
+ ],
113
+ "newTextItem": [], // No direct properties to set
114
+ "newVideoItem": [
115
+ "caption",
116
+ "video"
117
+ ],
118
+ "newImage": [
119
+ "altText",
120
+ "contentUri",
121
+ "properties",
122
+ "sourceUri"
123
+ ],
124
+ "newMediaProperties": [
125
+ "alignment",
126
+ "width"
127
+ ],
128
+ "newGrid": [
129
+ "columns",
130
+ "shuffleQuestions"
131
+ ],
132
+ "newChoiceQuestion": [
133
+ "options",
134
+ "shuffle",
135
+ "type"
136
+ ],
137
+ "newDateQuestion": [
138
+ "includeTime",
139
+ "includeYear"
140
+ ],
141
+ "newFileUploadQuestion": [
142
+ "folderId",
143
+ "maxFiles",
144
+ "maxFileSize",
145
+ "types"
146
+ ],
147
+ "newGrading": [
148
+ "correctAnswers",
149
+ "generalFeedback",
150
+ "pointValue",
151
+ "whenRight",
152
+ "whenWrong"
153
+ ],
154
+ "newRatingQuestion": [
155
+ "iconType",
156
+ "ratingScaleLevel"
157
+ ],
158
+ "newRowQuestion": [
159
+ "title"
160
+ ],
161
+ "newScaleQuestion": [
162
+ "high",
163
+ "highLabel",
164
+ "low",
165
+ "lowLabel"
166
+ ],
167
+ "newTextQuestion": [
168
+ "paragraph"
169
+ ],
170
+ "newTimeQuestion": [
171
+ "duration"
172
+ ],
173
+ "newCorrectAnswers": [
174
+ "answers"
175
+ ],
176
+ "newCorrectAnswer": [
177
+ "value"
178
+ ],
179
+ "newOption": [
180
+ "goToAction",
181
+ "goToSectionId",
182
+ "image",
183
+ "isOther",
184
+ "value"
185
+ ],
186
+ "newVideo": [
187
+ "properties",
188
+ "youtubeUri"
189
+ ],
190
+
191
+ }
192
+
193
+ Reflect.ownKeys(propLists).forEach(p => {
194
+ this[p] = () => advClassMaker(propLists[p])
195
+ })
196
+
197
+ }
198
+ toString() {
199
+ return 'AdvancedServiceIdentifier{name=forms, version=v1}'
200
+ }
201
+
202
+ getVersion() {
203
+ return 'v1'
204
+ }
205
+
206
+ get Form() {
207
+ return newFakeAdvFormsForm(this)
208
+ }
209
+ __addAllowed(id) {
210
+ if (ScriptApp.__behavior.sandBoxMode) {
211
+ ScriptApp.__behavior.addFile(id);
212
+ }
213
+ return id
214
+ }
215
+
216
+ __getFormsPerformance() {
217
+ return formsCacher.getPerformance()
218
+ }
219
+ }
220
+
221
+ export const newFakeAdvForms = (...args) => Proxies.guard(new FakeAdvForms(...args))