@mcpher/gas-fakes 2.5.3 → 2.5.5

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 (53) hide show
  1. package/.claspignore +1 -1
  2. package/README.md +36 -3
  3. package/exgcp.sh +63 -0
  4. package/package.json +1 -1
  5. package/src/services/content/contentservice.js +3 -2
  6. package/src/services/documentapp/appenderhelpers.js +8 -3
  7. package/src/services/documentapp/elementhelpers.js +148 -7
  8. package/src/services/documentapp/elementoptions.js +40 -0
  9. package/src/services/documentapp/elements.js +7 -0
  10. package/src/services/documentapp/fakebookmark.js +1 -3
  11. package/src/services/documentapp/fakecontainerelement.js +188 -0
  12. package/src/services/documentapp/fakedate.js +92 -0
  13. package/src/services/documentapp/fakedocument.js +51 -0
  14. package/src/services/documentapp/fakedocumenttab.js +9 -25
  15. package/src/services/documentapp/fakeelement.js +453 -90
  16. package/src/services/documentapp/fakeequation.js +28 -0
  17. package/src/services/documentapp/fakeequationfunction.js +37 -0
  18. package/src/services/documentapp/fakeequationfunctionargumentseparator.js +28 -0
  19. package/src/services/documentapp/fakeequationsymbol.js +37 -0
  20. package/src/services/documentapp/fakefootersection.js +64 -1
  21. package/src/services/documentapp/fakeheadersection.js +64 -0
  22. package/src/services/documentapp/fakeperson.js +67 -0
  23. package/src/services/documentapp/fakerangeelement.js +27 -1
  24. package/src/services/documentapp/fakerichlink.js +79 -0
  25. package/src/services/documentapp/fakesectionelement.js +51 -12
  26. package/src/services/documentapp/faketablecell.js +98 -0
  27. package/src/services/documentapp/nrhelpers.js +2 -1
  28. package/src/services/documentapp/shadowdocument.js +19 -2
  29. package/src/services/enums/contentenums.js +1 -3
  30. package/src/services/enums/scriptenums.js +31 -4
  31. package/src/services/enums/xmlenums.js +14 -0
  32. package/src/services/scriptapp/app.js +14 -7
  33. package/src/services/scriptapp/fakeauthorizationinfo.js +4 -4
  34. package/src/services/spreadsheetapp/fakeembeddedchartbuilder.js +87 -12
  35. package/src/services/spreadsheetapp/fakespreadsheet.js +360 -62
  36. package/src/services/spreadsheetapp/fakespreadsheettheme.js +53 -0
  37. package/src/services/urlfetchapp/app.js +216 -175
  38. package/src/services/xmlservice/app.js +3 -78
  39. package/src/services/xmlservice/fakeattribute.js +15 -0
  40. package/src/services/xmlservice/fakecdata.js +40 -0
  41. package/src/services/xmlservice/fakecomment.js +34 -0
  42. package/src/services/xmlservice/fakecontent.js +51 -0
  43. package/src/services/xmlservice/fakedoctype.js +68 -0
  44. package/src/services/xmlservice/fakedocument.js +110 -13
  45. package/src/services/xmlservice/fakeelement.js +297 -82
  46. package/src/services/xmlservice/fakeentityref.js +54 -0
  47. package/src/services/xmlservice/fakeformat.js +67 -22
  48. package/src/services/xmlservice/fakeprocessinginstruction.js +44 -0
  49. package/src/services/xmlservice/faketext.js +39 -0
  50. package/src/services/xmlservice/fakexmlservice.js +118 -0
  51. package/src/support/sxfetch.js +60 -0
  52. package/tools/omlx.env.example +6 -0
  53. package/tools/omlx_mcp_server.cjs +157 -0
@@ -1,217 +1,258 @@
1
1
  // fake Apps Script UrlFetchApp
2
2
 
3
- import { Auth } from '../../support/auth.js'
4
3
  import { Syncit } from '../../support/syncit.js'
5
4
  import { Proxies } from '../../support/proxies.js'
6
5
 
7
- // Note that all async type functions have been converted to synch ro make it Apps Script like
8
-
9
-
10
6
  /**
11
- * make got response look like UrlFetchApp response
12
- * @param {Response} reponse
13
- * @return {FakeHTTPResponse} UrlFetchApp flavor
7
+ * Helper function to fix and normalize HTTP headers.
8
+ * @param {object} response - The raw response object.
9
+ * @returns {object} Normalized headers object.
14
10
  */
15
- const responsify = (response) => {
16
- if (!response) {
17
- // Return a dummy response that identifies as an error
18
- return {
19
- getAllHeaders: () => ({}),
20
- getResponseCode: () => 500,
21
- getContentText: () => "UrlFetchApp: No response data available.",
22
- getHeaders: () => ({}),
23
- getBlob: () => null,
24
- getContent: () => []
25
- }
11
+ const fixHeaders = (response) => {
12
+ // it's a map with a pair of values
13
+ const headers = (response.rawHeaders || [])
14
+ if (headers.length % 2 !== 0) {
15
+ throw `fixHeaders:invalid number of header value pairs - ${headers.length}`
26
16
  }
17
+ // split into pairs and return as object
18
+ const mapHeaders = new Map(headers.flatMap((_, i, a) => i % 2 ? [] : [a.slice(i, i + 2)]));
19
+ return Object.fromEntries(mapHeaders)
20
+ }
27
21
 
28
- // getAllHeaders() Object Returns an attribute/value map of headers for the HTTP response, with headers that have multiple values returned as arrays.
29
- const getAllHeaders = () => fixHeaders(response)
22
+ /**
23
+ * Helper function to convert raw response data into a FakeBlob.
24
+ * Assumes Utilities.newBlob is available in the scope.
25
+ * @param {object} response - The raw response object.
26
+ * @param {object} headers - The normalized headers.
27
+ * @returns {object} A FakeBlob representation.
28
+ */
29
+ const blobify = (response, headers) => {
30
+ if (!headers) return null
31
+ // the name is buried in the Conent-Disposition
32
+ const disp = headers["Content-Disposition"]
33
+ const filename = disp && disp.replace(/.*filename="([^"]*).*/, "$1")
34
+ const name = filename || ""
35
+ const contentType = headers["Content-Type"]?.replace(/([^;]*).*/, "$1").trim()
36
+ const bytes = response.rawBody || response.body || null
37
+ // Assuming Utilities is available globally or imported elsewhere
38
+ return Utilities.newBlob(bytes, contentType, name)
39
+ }
30
40
 
31
- // getResponseCode() Integer Get the HTTP status code (200 for OK, etc.) of an HTTP response
32
- const getResponseCode = () => response.statusCode || response.status || 500
33
41
 
34
- // getContentText() String Gets the content of an HTTP response encoded as a string.
35
- const getContentText = () => blobify(response).getDataAsString()
42
+ /**
43
+ * Represents a fake HTTP response object, mimicking UrlFetchApp's response structure.
44
+ */
45
+ export class FakeHTTPResponse {
46
+ /**
47
+ * @param {object} response - The raw response data from Syncit.
48
+ */
49
+ constructor(response) {
50
+ this._response = response
51
+ this._headers = null
52
+ }
36
53
 
37
- // getHeaders() Object Returns an attribute/value map of headers for the HTTP response.
38
- const getHeaders = () => fixHeaders(response)
54
+ /**
55
+ * @returns {object} Attribute/value map of headers.
56
+ */
57
+ getAllHeaders() {
58
+ if (!this._response) return {}
59
+ if (!this._headers) {
60
+ this._headers = fixHeaders(this._response)
61
+ }
62
+ return this._headers
63
+ }
39
64
 
40
- // getContent() Bytes[] the data as byte array
41
- const getContent = () => response.rawBody || response.body
65
+ /**
66
+ * @returns {number} The HTTP status code.
67
+ */
68
+ getResponseCode() {
69
+ if (!this._response) return 500
70
+ return this._response.statusCode || this._response.status || 500
71
+ }
42
72
 
43
- // getBlob () FakeBlob the content as a blob
44
- const getBlob = () => blobify(response)
73
+ /**
74
+ * @returns {string} The content of an HTTP response encoded as a string.
75
+ */
76
+ getContentText() {
77
+ if (!this._response) return "UrlFetchApp: No response data available."
78
+ const headers = this.getAllHeaders()
79
+ const blob = blobify(this._response, headers)
80
+ return blob ? blob.getDataAsString() : ""
81
+ }
45
82
 
46
- // got returns lower case headers props, so we have to resort to using rawheaders so we match the case of Apps Script
47
- // TODO - find an example in apps script where getAllHeaders is not equal to getHeaders
48
- const fixHeaders = (response) => {
49
- // it's a map with a pair of values
50
- const headers = (response.rawHeaders || [])
51
- if (headers % 2) {
52
- throw `fixHeaders:invalid number of header value pairs - ${headers.length}`
53
- }
54
- // split into pairs and return as object
55
- const mapHeaders = new Map(headers.flatMap((_, i, a) => i % 2 ? [] : [a.slice(i, i + 2)]));
56
- return Object.fromEntries(mapHeaders)
83
+ /**
84
+ * @returns {object} Attribute/value map of headers.
85
+ */
86
+ getHeaders() {
87
+ return this.getAllHeaders()
57
88
  }
58
- const blobify = (response) => {
59
- const headers = fixHeaders(response)
60
- if (!headers) return null
61
- // the name is buried in the Conent-Disposition
62
- const disp = headers["Content-Disposition"]
63
- const filename = disp && disp.replace(/.*filename="([^"]*).*/, "$1")
64
- const name = filename || ""
65
- const contentType = headers["Content-Type"]?.replace(/([^;]*).*/, "$1").trim()
66
- const bytes = response.rawBody || response.body || null
67
- return Utilities.newBlob(bytes, contentType, name)
89
+
90
+ /**
91
+ * @returns {ArrayBuffer | Uint8Array} The raw binary content of an HTTP response.
92
+ */
93
+ getContent() {
94
+ if (!this._response) return []
95
+ return this._response.rawBody || this._response.body
68
96
  }
69
97
 
70
- /* TODO
71
- getAs(contentType) Blob Return the data inside this object as a blob converted to the specified content type.
72
- getBlob() Blob Return the data inside this object as a blob.
73
- getContent() Byte[] Gets the raw binary content of an HTTP response.
74
- getContentText(charset) String Returns the content of an HTTP response encoded as a string of the given charset.
75
- */
76
- return {
77
- getAllHeaders,
78
- getResponseCode,
79
- getContentText,
80
- getHeaders,
81
- getBlob,
82
- getContent
98
+ /**
99
+ * @returns {object} A FakeBlob representing the content.
100
+ */
101
+ getBlob() {
102
+ if (!this._response) return null
103
+ const headers = this.getAllHeaders()
104
+ return blobify(this._response, headers)
83
105
  }
84
106
  }
85
107
 
86
- // this has been syncified
87
- const fetch = (url, options = {}) => {
108
+ /**
109
+ * Represents a fake UrlFetchApp service, mimicking the global Apps Script service.
110
+ */
111
+ export class FakeUrlFetchApp {
112
+ /**
113
+ * @param {object} [options] - Configuration options (optional).
114
+ */
115
+ constructor(options = {}) {
116
+ this.options = options
117
+ }
88
118
 
89
- // check options for method and provide default
90
- options.method = options.method || "get"
119
+ /**
120
+ * Performs a GET request.
121
+ * @param {string} url - The URL to fetch.
122
+ * @param {object} [options] - Request options.
123
+ * @returns {FakeHTTPResponse} The response object.
124
+ */
125
+ fetch(url, options = {}) {
126
+ // check options for method and provide default
127
+ options.method = options.method || "get"
91
128
 
92
- const responseFields = [
93
- 'rawHeaders',
94
- 'statusCode',
95
- 'body',
96
- 'headers',
97
- 'rawBody'
98
- ]
129
+ const responseFields = [
130
+ 'rawHeaders',
131
+ 'statusCode',
132
+ 'body',
133
+ 'headers',
134
+ 'rawBody'
135
+ ]
99
136
 
100
- const { data } = Syncit.fxFetch(url, options, responseFields)
101
- return responsify(data)
102
- }
137
+ const { data } = Syncit.fxFetch(url, options, responseFields)
138
+ return new FakeHTTPResponse(data)
139
+ }
103
140
 
104
- // this has been syncified
105
- const fetchAll = (requests) => {
141
+ /**
142
+ * Performs multiple requests concurrently.
143
+ * @param {Array<object>} requests - Array of request configurations.
144
+ * @returns {Array<FakeHTTPResponse>} Array of response objects.
145
+ */
146
+ fetchAll(requests) {
147
+ const responseFields = [
148
+ 'rawHeaders',
149
+ 'statusCode',
150
+ 'body',
151
+ 'headers',
152
+ 'rawBody'
153
+ ]
106
154
 
107
- const responseFields = [
108
- 'rawHeaders',
109
- 'statusCode',
110
- 'body',
111
- 'headers',
112
- 'rawBody'
113
- ]
155
+ const responses = Syncit.fxFetchAll(requests, responseFields)
156
+ return responses.map(({ data }) => new FakeHTTPResponse(data))
157
+ }
114
158
 
115
- const responses = Syncit.fxFetchAll(requests, responseFields)
116
- return responses.map(({ data }) => responsify(data))
117
- }
159
+ /**
160
+ * Generates a standardized request object for UrlFetchApp.
161
+ * @param {string} url - The target URL.
162
+ * @param {object} [options] - Request options.
163
+ * @returns {object} The standardized request object.
164
+ */
165
+ getRequest(url, options = {}) {
166
+ const defaultMethod = 'get';
167
+ const request = {
168
+ url: url,
169
+ method: (options.method || defaultMethod).toLowerCase(),
170
+ headers: options.headers || {},
171
+ };
118
172
 
119
- const getRequest = (url, options = {}) => {
120
- const defaultMethod = 'get';
121
- const request = {
122
- url: url,
123
- method: (options.method || defaultMethod).toLowerCase(),
124
- headers: options.headers || {},
125
- };
126
-
127
- // Apps Script defaults
128
- if (options.contentType) {
129
- request.contentType = options.contentType;
130
- }
131
-
132
- if (options.payload) {
133
- request.payload = options.payload;
134
- }
135
-
136
- if (options.useIntranet !== undefined) {
137
- request.useIntranet = options.useIntranet;
138
- }
173
+ // Apps Script defaults
174
+ if (options.contentType) {
175
+ request.contentType = options.contentType;
176
+ }
139
177
 
140
- // Add standard fetch behavior
141
- if (request.method === 'post' || request.method === 'put' || request.method === 'patch') {
142
- if (!request.contentType) {
143
- request.contentType = 'application/x-www-form-urlencoded';
144
- }
145
- }
178
+ if (options.payload !== undefined && options.payload !== null) {
179
+ const payload = options.payload;
146
180
 
147
- return request;
181
+ // 1. Handle Blob payload
182
+ if (typeof payload.getDataAsString === 'function') {
183
+ request.payload = payload.getDataAsString();
184
+ }
185
+ // 2. Handle Byte[] array payload (Array of numbers)
186
+ else if (Array.isArray(payload) && payload.every(item => typeof item === 'number')) {
187
+ // Convert byte array to UTF-8 string
188
+ request.payload = Buffer.from(payload).toString('utf8');
189
+ }
190
+ // 3. Handle plain JavaScript object payload
191
+ else if (typeof payload === 'object' && !Buffer.isBuffer(payload)) {
192
+ // Serialize object to form-encoded string
193
+ const params = new URLSearchParams();
194
+ for (const key in payload) {
195
+ if (Object.prototype.hasOwnProperty.call(payload, key)) {
196
+ params.append(key, payload[key]);
197
+ }
198
+ }
199
+ request.payload = params.toString();
200
+
201
+ // Set default content type for form data
202
+ if (!request.contentType) {
203
+ request.contentType = 'application/x-www-form-urlencoded';
204
+ }
205
+ }
206
+ // Default case: pass payload directly (e.g., string, Buffer, etc.)
207
+ else {
208
+ request.payload = payload;
209
+ }
210
+ } else if (options.payload === null) {
211
+ request.payload = null;
212
+ }
213
+
214
+ if (options.useIntranet !== undefined) {
215
+ request.useIntranet = options.useIntranet;
216
+ }
217
+
218
+ // Add standard fetch behavior
219
+ if (request.method === 'post' || request.method === 'put' || request.method === 'patch') {
220
+ if (!request.contentType) {
221
+ request.contentType = 'application/x-www-form-urlencoded';
222
+ }
223
+ }
224
+
225
+ return request;
226
+ }
148
227
  }
149
228
 
229
+ /**
230
+ * Helper function to create and return a new instance of FakeUrlFetchApp.
231
+ * @returns {FakeUrlFetchApp}
232
+ */
233
+ export const newFakeUrlFetchApp = () => new FakeUrlFetchApp();
234
+
150
235
 
151
236
  // This will eventually hold a proxy for DriveApp
152
237
  let _app = null
153
238
 
154
239
  /**
155
- * adds to global space to mimic Apps Script behavior
240
+ * Gets the singleton instance of FakeUrlFetchApp.
241
+ * @returns {FakeUrlFetchApp}
156
242
  */
157
- const name = "UrlFetchApp"
158
- if (typeof globalThis[name] === typeof undefined) {
159
-
160
- const getApp = () => {
161
- // if it hasne been intialized yet then do that
162
- if (!_app) {
163
- _app = {
164
- fetch,
165
- fetchAll,
166
- getRequest
167
- }
168
- }
169
- // this is the actual driveApp we'll return from the proxy
170
- return _app
243
+ const getApp = () => {
244
+ // if it hasn't been initialized yet then do that
245
+ if (!_app) {
246
+ _app = newFakeUrlFetchApp()
171
247
  }
248
+ // return the actual driveApp we'll return from the proxy
249
+ return _app
250
+ }
172
251
 
252
+ /**
253
+ * Adds to global space to mimic Apps Script behavior
254
+ */
255
+ const name = "UrlFetchApp"
256
+ if (typeof globalThis[name] === typeof undefined) {
173
257
  Proxies.registerProxy(name, getApp)
174
-
175
258
  }
176
-
177
-
178
- /** got reponse props
179
- [ '_events', 'object' ],
180
- [ '_readableState', 'object' ],
181
- [ '_writableState', 'object' ],
182
- [ 'allowHalfOpen', 'boolean' ],
183
- [ '_destroy', 'function' ],
184
- [ '_maxListeners', 'undefined' ],
185
- [ '_eventsCount', 'number' ],
186
- [ 'socket', 'object' ],
187
- [ 'httpVersionMajor', 'number' ],
188
- [ 'httpVersionMinor', 'number' ],
189
- [ 'httpVersion', 'string' ],
190
- [ 'complete', 'boolean' ],
191
- [ 'rawHeaders', 'object' ],
192
- [ 'rawTrailers', 'object' ],
193
- [ 'joinDuplicateHeaders', 'undefined' ],
194
- [ 'aborted', 'boolean' ],
195
- [ 'upgrade', 'boolean' ],
196
- [ 'url', 'string' ],
197
- [ 'method', 'object' ],
198
- [ 'statusCode', 'number' ],
199
- [ 'statusMessage', 'string' ],
200
- [ 'client', 'object' ],
201
- [ '_consuming', 'boolean' ],
202
- [ '_dumped', 'boolean' ],
203
- [ 'req', 'object' ],
204
- [ 'timings', 'object' ],
205
- [ 'headers', 'object' ],
206
- [ 'setTimeout', 'function' ],
207
- [ 'trailers', 'object' ],
208
- [ 'requestUrl', 'object' ],
209
- [ 'redirectUrls', 'object' ],
210
- [ 'request', 'object' ],
211
- [ 'isFromCache', 'boolean' ],
212
- [ 'ip', 'string' ],
213
- [ 'retryCount', 'number' ],
214
- [ 'ok', 'boolean' ],
215
- [ 'rawBody', 'object' ],
216
- [ 'body', 'string' ]
217
- */
@@ -1,81 +1,6 @@
1
1
  // fake Apps Script XmlService
2
- import { XMLParser } from 'fast-xml-parser';
3
- import { Proxies } from '../../support/proxies.js';
4
- import { FakeDocument } from './fakedocument.js';
5
- import { FakeElement } from './fakeelement.js';
6
- import { FakeNamespace } from './fakenamespace.js';
7
- import { FakeFormat } from './fakeformat.js';
2
+ import { newFakeXmlService as maker } from './fakexmlservice.js';
3
+ import { lazyLoaderApp } from '../common/lazyloader.js';
8
4
 
9
- /**
10
- * Parses the given XML string and returns a Document object.
11
- * @param {string} xml The XML string to parse.
12
- * @return {FakeDocument} The parsed document.
13
- */
14
- const parse = (xml) => {
15
- const options = {
16
- ignoreAttributes: false,
17
- attributeNamePrefix: "@_",
18
- textNodeName: "#text",
19
- alwaysCreateTextNode: true,
20
- removeNSPrefix: false // We need prefixes to map them in FakeElement
21
- };
22
- const parser = new XMLParser(options);
23
- const jsonObj = parser.parse(xml);
24
-
25
- // The jsonObj will have the root element name as a key
26
- const rootName = Object.keys(jsonObj).find(key => !key.startsWith('?')); // Ignore processing instructions
27
- if (!rootName) {
28
- throw new Error("XmlService: No root element found in XML.");
29
- }
30
-
31
- const rootData = jsonObj[rootName];
32
- const rootElement = new FakeElement(rootName, rootData);
33
- return new FakeDocument(rootElement);
34
- };
35
-
36
- /**
37
- * Returns a Namespace object with the given prefix and URI.
38
- * @param {string} prefix The prefix.
39
- * @param {string} uri The URI.
40
- * @return {FakeNamespace} The namespace.
41
- */
42
- const getNamespace = (prefix, uri) => {
43
- return new FakeNamespace(prefix, uri);
44
- };
45
-
46
- /**
47
- * Returns a Format object for pretty printing.
48
- * @return {FakeFormat} The format object.
49
- */
50
- const getPrettyFormat = () => {
51
- return new FakeFormat({ pretty: true });
52
- };
53
-
54
- /**
55
- * Returns a Format object for raw output.
56
- * @return {FakeFormat} The format object.
57
- */
58
- const getRawFormat = () => {
59
- return new FakeFormat({ pretty: false });
60
- };
61
-
62
- // Singleton app object
63
5
  let _app = null;
64
-
65
- const name = "XmlService";
66
- if (typeof globalThis[name] === typeof undefined) {
67
- const getApp = () => {
68
- if (!_app) {
69
- _app = {
70
- parse,
71
- getNamespace,
72
- getPrettyFormat,
73
- getRawFormat,
74
- toString: () => name
75
- };
76
- }
77
- return _app;
78
- };
79
-
80
- Proxies.registerProxy(name, getApp);
81
- }
6
+ _app = lazyLoaderApp(_app, 'XmlService', maker);
@@ -0,0 +1,15 @@
1
+ import { FakeNamespace } from './fakenamespace.js';
2
+
3
+ export class FakeAttribute {
4
+ constructor(name, value, namespace = null) {
5
+ this._name = name;
6
+ this._value = value;
7
+ this._namespace = namespace || new FakeNamespace("", "");
8
+ }
9
+ getName() { return this._name; }
10
+ getValue() { return this._value; }
11
+ getNamespace() { return this._namespace; }
12
+ setName(name) { this._name = name; return this; }
13
+ setValue(value) { this._value = value; return this; }
14
+ setNamespace(namespace) { this._namespace = namespace; return this; }
15
+ }
@@ -0,0 +1,40 @@
1
+ import { FakeContent } from './fakecontent.js';
2
+ import * as Enums from '../enums/xmlenums.js';
3
+
4
+ export class FakeCdata extends FakeContent {
5
+ constructor(text, parentElement = null) {
6
+ super(Enums.ContentTypes.CDATA);
7
+ this._text = text || '';
8
+ this._parent = parentElement;
9
+ }
10
+
11
+ append(text) {
12
+ this._text += text || '';
13
+ return this;
14
+ }
15
+
16
+ getText() {
17
+ return this._text;
18
+ }
19
+
20
+ getValue() {
21
+ return this._text;
22
+ }
23
+
24
+ setText(text) {
25
+ this._text = text || '';
26
+ return this;
27
+ }
28
+
29
+ detach() {
30
+ return super.detach();
31
+ }
32
+
33
+ getParentElement() {
34
+ return super.getParentElement();
35
+ }
36
+
37
+ toString() {
38
+ return `[CDATA: ${this._text}]`;
39
+ }
40
+ }
@@ -0,0 +1,34 @@
1
+ import { FakeContent } from './fakecontent.js';
2
+ import * as Enums from '../enums/xmlenums.js';
3
+
4
+ export class FakeComment extends FakeContent {
5
+ constructor(text = '') {
6
+ super(Enums.ContentTypes.COMMENT);
7
+ this._text = text;
8
+ }
9
+
10
+ getText() {
11
+ return this._text;
12
+ }
13
+
14
+ setText(text) {
15
+ this._text = text || '';
16
+ return this;
17
+ }
18
+
19
+ getValue() {
20
+ return this._text;
21
+ }
22
+
23
+ detach() {
24
+ return super.detach();
25
+ }
26
+
27
+ getParentElement() {
28
+ return super.getParentElement();
29
+ }
30
+
31
+ toString() {
32
+ return `[Comment: <!--${this._text}-->]`;
33
+ }
34
+ }
@@ -0,0 +1,51 @@
1
+ import * as Enums from '../enums/xmlenums.js';
2
+
3
+ /**
4
+ * Base class for all XML content nodes (Text, Element, Comment, etc.).
5
+ * Provides core functionality for content representation and type casting.
6
+ */
7
+ export class FakeContent {
8
+ /**
9
+ * @param {string} type - The content type of this node.
10
+ */
11
+ constructor(type) {
12
+ this._type = type;
13
+ this._parent = null;
14
+ }
15
+
16
+ getType() {
17
+ return this._type;
18
+ }
19
+
20
+ getParentElement() {
21
+ if (this._parent && this._parent.constructor && this._parent.constructor.name === 'FakeDocument') {
22
+ return null;
23
+ }
24
+ return this._parent;
25
+ }
26
+
27
+ setParentElement(parent) {
28
+ this._parent = parent;
29
+ }
30
+
31
+ detach() {
32
+ if (this._parent && typeof this._parent.removeContent === 'function') {
33
+ this._parent.removeContent(this);
34
+ }
35
+ this._parent = null;
36
+ return this;
37
+ }
38
+
39
+ // --- Type Casting Methods ---
40
+ asCdata() { return this._type === Enums.ContentTypes.CDATA ? this : null; }
41
+ asComment() { return this._type === Enums.ContentTypes.COMMENT ? this : null; }
42
+ asDocType() { return this._type === Enums.ContentTypes.DOCTYPE ? this : null; }
43
+ asElement() { return this._type === Enums.ContentTypes.ELEMENT ? this : null; }
44
+ asEntityRef() { return this._type === Enums.ContentTypes.ENTITYREF ? this : null; }
45
+ asProcessingInstruction() { return this._type === Enums.ContentTypes.PROCESSINGINSTRUCTION ? this : null; }
46
+ asText() { return this._type === Enums.ContentTypes.TEXT ? this : null; }
47
+
48
+ getValue() {
49
+ return '';
50
+ }
51
+ }