@mcpher/gas-fakes 1.2.28 → 1.2.30

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "node": ">=20.11.0"
4
4
  },
5
5
  "dependencies": {
6
- "@mcpher/fake-gasenum": "^1.0.2",
6
+ "@mcpher/fake-gasenum": "^1.0.6",
7
7
  "@mcpher/gas-flex-cache": "^1.1.3",
8
8
  "@modelcontextprotocol/sdk": "^1.20.2",
9
9
  "@sindresorhus/is": "^7.0.1",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "name": "@mcpher/gas-fakes",
36
36
  "author": "bruce mcpherson",
37
- "version": "1.2.28",
37
+ "version": "1.2.30",
38
38
  "license": "MIT",
39
39
  "main": "main.js",
40
40
  "description": "A proof of concept implementation of Apps Script Environment on Node",
@@ -1,7 +1,7 @@
1
1
  import { Proxies } from '../../support/proxies.js';
2
2
  import { newFakeCalendarEvent } from './fakecalendarevent.js';
3
3
  import { newFakeCalendarEventSeries } from './fakecalendareventseries.js';
4
-
4
+ import { Utils } from '../../support/utils.js';
5
5
  export const newFakeCalendar = (...args) => {
6
6
  return Proxies.guard(new FakeCalendar(...args));
7
7
  };
@@ -16,7 +16,9 @@ export class FakeCalendar {
16
16
  * @param {object} resource The underlying Calendar resource (Advanced).
17
17
  */
18
18
  constructor(id, resource) {
19
- this.__id = id;
19
+ // If a resource is provided, use its canonical ID (e.g. email) instead of the lookup ID (e.g. 'primary')
20
+ // This matches GAS behavior where getId() returns the email even if retrieved via 'primary'
21
+ this.__id = (resource && resource.id) ? resource.id : id;
20
22
  }
21
23
 
22
24
  /**
@@ -81,10 +83,13 @@ export class FakeCalendar {
81
83
  const entry = this.__listEntry;
82
84
  return entry ? entry.backgroundColor : '';
83
85
  }
84
-
86
+ // this can be an enum, or a hex code
85
87
  setColor(color) {
86
88
  this.__checkWriteAccess();
87
- Calendar.CalendarList.patch({ backgroundColor: color }, this.getId());
89
+ if (Utils.isEnum(color)) {
90
+ color = color.toString();
91
+ }
92
+ Calendar.CalendarList.patch({ backgroundColor: color }, this.getId(), {colorRgbFormat: true});
88
93
  return this;
89
94
  }
90
95
 
@@ -123,11 +128,13 @@ export class FakeCalendar {
123
128
  * Permanently deletes a calendar.
124
129
  */
125
130
  deleteCalendar() {
131
+ this.__checkUsage('write');
126
132
  this.__checkDeleteAccess();
127
- Calendar.Calendars.remove(this.getId());
133
+ Calendar.Calendars.delete(this.getId());
128
134
  }
129
135
 
130
136
  unsubscribeFromCalendar() {
137
+ this.__checkUsage('write');
131
138
  // Unsubscribes the user from a calendar (removes from list).
132
139
  Calendar.CalendarList.remove(this.getId());
133
140
  }
@@ -138,6 +145,7 @@ export class FakeCalendar {
138
145
  if (startTime.getTime() >= endTime.getTime()) {
139
146
  throw new Error('Event start date must be before event end date.');
140
147
  }
148
+ this.__checkUsage('write');
141
149
  this.__checkWriteAccess();
142
150
  const resource = {
143
151
  summary: title,
@@ -156,6 +164,7 @@ export class FakeCalendar {
156
164
  }
157
165
 
158
166
  createAllDayEvent(title, startDate, endDateOrOptions, options) {
167
+ this.__checkUsage('write');
159
168
  this.__checkWriteAccess();
160
169
 
161
170
  let endDate = startDate;
@@ -199,12 +208,14 @@ export class FakeCalendar {
199
208
  }
200
209
 
201
210
  createEventFromDescription(description) {
211
+ this.__checkUsage('write');
202
212
  this.__checkWriteAccess();
203
213
  const event = Calendar.Events.quickAdd(this.getId(), description);
204
214
  return newFakeCalendarEvent(this.getId(), event);
205
215
  }
206
216
 
207
217
  getEvents(startTime, endTime, options) {
218
+ this.__checkUsage('read');
208
219
  this.__checkReadAccess();
209
220
  const args = {
210
221
  timeMin: startTime.toISOString(),
@@ -232,6 +243,7 @@ export class FakeCalendar {
232
243
  }
233
244
 
234
245
  getEventById(iCalId) {
246
+ this.__checkUsage('read');
235
247
  this.__checkReadAccess();
236
248
  // Apps Script expects iCalUID. Try finding by iCalUID first using list.
237
249
  // This avoids "Not Found" errors in the worker when passing iCalUID to events.get (which expects eventId)
@@ -252,18 +264,48 @@ export class FakeCalendar {
252
264
  // --- Series ---
253
265
 
254
266
  createEventSeries(title, startTime, endTime, recurrence, options) {
255
- // TODO: Implement recurrence conversion to RRULE
256
- // For now, simple insert without proper RRULE if Recurrence object not fully implemented
257
- return this.createEvent(title, startTime, endTime, options); // Fallback
267
+ this.__checkUsage('write');
268
+ this.__checkWriteAccess();
269
+ const resource = {
270
+ summary: title,
271
+ start: { dateTime: startTime.toISOString() },
272
+ end: { dateTime: endTime.toISOString() },
273
+ recurrence: [ recurrence.toString() ] // Assuming recurrence has proper RRULE string representation or handled
274
+ };
275
+ this.__applyEventOptions(resource, options);
276
+ const event = Calendar.Events.insert(resource, this.getId());
277
+ return newFakeCalendarEventSeries(this.getId(), event);
258
278
  }
259
279
 
260
280
  createAllDayEventSeries(title, startDate, recurrence, options) {
261
- // TODO
262
- return this.createAllDayEvent(title, startDate, options); // Fallback
281
+ this.__checkUsage('write');
282
+ this.__checkWriteAccess();
283
+ const toDateString = (date) => date.toISOString().split('T')[0];
284
+ // For all day series, end date is usually next day for the first instance
285
+ const endDate = new Date(startDate);
286
+ endDate.setDate(endDate.getDate() + 1);
287
+
288
+ const resource = {
289
+ summary: title,
290
+ start: { date: toDateString(startDate) },
291
+ end: { date: toDateString(endDate) },
292
+ recurrence: [ recurrence.toString() ]
293
+ };
294
+ this.__applyEventOptions(resource, options);
295
+ const event = Calendar.Events.insert(resource, this.getId());
296
+ return newFakeCalendarEventSeries(this.getId(), event);
263
297
  }
264
298
 
265
299
  getEventSeriesById(iCalId) {
266
- // TODO
300
+ this.__checkUsage('read');
301
+ this.__checkReadAccess();
302
+ try {
303
+ const event = Calendar.Events.get(this.getId(), iCalId);
304
+ // Check if it's actually a recurring event (has recurrence or is instance)
305
+ if (event.recurrence || event.recurringEventId) {
306
+ return newFakeCalendarEventSeries(this.getId(), event);
307
+ }
308
+ } catch (e) {}
267
309
  return null;
268
310
  }
269
311
 
@@ -277,6 +319,34 @@ export class FakeCalendar {
277
319
  }
278
320
  }
279
321
 
322
+ __checkUsage(type) {
323
+ const serviceName = 'CalendarApp';
324
+ const behavior = ScriptApp.__behavior;
325
+ if (behavior.sandboxMode) {
326
+ const settings = behavior.sandboxService[serviceName];
327
+ let limit = settings && settings.usageLimit;
328
+ if (limit) {
329
+ if (typeof limit === 'number') {
330
+ const total = (settings.usageCount.read || 0) + (settings.usageCount.write || 0) + (settings.usageCount.trash || 0);
331
+ if (total >= limit) {
332
+ throw new Error(`Calendar total usage limit of ${limit} exceeded`);
333
+ }
334
+ settings.incrementUsage(type);
335
+ return;
336
+ }
337
+
338
+ let specificLimit = limit[type];
339
+ if (specificLimit !== undefined) {
340
+ const current = settings.usageCount[type] || 0;
341
+ if (current >= specificLimit) {
342
+ throw new Error(`Calendar ${type} usage limit of ${specificLimit} exceeded`);
343
+ }
344
+ settings.incrementUsage(type);
345
+ }
346
+ }
347
+ }
348
+ }
349
+
280
350
  __checkAccess(accessType) {
281
351
  const behavior = ScriptApp.__behavior;
282
352
  if (!behavior.sandboxMode) return true;
@@ -1,6 +1,7 @@
1
1
  import { Proxies } from '../../support/proxies.js';
2
2
  import * as CalendarEnums from '../enums/calendarenums.js';
3
3
  import { newFakeCalendar } from './fakecalendar.js';
4
+ import { newFakeEventRecurrence } from './fakeeventrecurrence.js';
4
5
 
5
6
  /**
6
7
  * Creates a new FakeCalendarApp instance.
@@ -20,16 +21,31 @@ export class FakeCalendarApp {
20
21
  Object.assign(this, CalendarEnums);
21
22
  }
22
23
 
24
+ // --- Service Methods ---
25
+
26
+ newRecurrence() {
27
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'newRecurrence');
28
+ return newFakeEventRecurrence();
29
+ }
30
+
23
31
  createCalendar(name, options = {}) {
24
32
  ScriptApp.__behavior.checkMethod('CalendarApp', 'createCalendar');
25
33
  this.__checkUsage('write');
26
34
  const resource = Calendar.Calendars.insert({ summary: name, ...options });
27
- if (ScriptApp.__behavior.sandboxMode && resource.id) {
35
+ if (resource.id) {
28
36
  ScriptApp.__behavior.addCalendarId(resource.id);
29
37
  }
30
38
  return newFakeCalendar(resource.id, resource);
31
39
  }
32
40
 
41
+ subscribeToCalendar(id) {
42
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'subscribeToCalendar');
43
+ this.__checkUsage('write');
44
+ const resource = { id };
45
+ Calendar.CalendarList.insert(resource);
46
+ return newFakeCalendar(id);
47
+ }
48
+
33
49
  getCalendarById(id) {
34
50
  ScriptApp.__behavior.checkMethod('CalendarApp', 'getCalendarById');
35
51
  this.__checkUsage('read');
@@ -53,6 +69,19 @@ export class FakeCalendarApp {
53
69
  .map(item => newFakeCalendar(item.id, item));
54
70
  }
55
71
 
72
+ getOwnedCalendarsByName(name) {
73
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getOwnedCalendarsByName');
74
+ this.__checkUsage('read');
75
+ return this.getCalendarsByName(name).filter(c => c.isOwnedByMe());
76
+ }
77
+
78
+ getOwnedCalendarById(id) {
79
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getOwnedCalendarById');
80
+ this.__checkUsage('read');
81
+ const cal = this.getCalendarById(id);
82
+ return cal && cal.isOwnedByMe() ? cal : null;
83
+ }
84
+
56
85
  getAllCalendars() {
57
86
  ScriptApp.__behavior.checkMethod('CalendarApp', 'getAllCalendars');
58
87
  this.__checkUsage('read');
@@ -72,6 +101,109 @@ export class FakeCalendarApp {
72
101
  .map(item => newFakeCalendar(item.id, item));
73
102
  }
74
103
 
104
+ // --- Default Calendar Delegates ---
105
+
106
+ createAllDayEvent(...args) {
107
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'createAllDayEvent');
108
+ return this.getDefaultCalendar().createAllDayEvent(...args);
109
+ }
110
+ createAllDayEventSeries(...args) {
111
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'createAllDayEventSeries');
112
+ return this.getDefaultCalendar().createAllDayEventSeries(...args);
113
+ }
114
+ createEvent(...args) {
115
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'createEvent');
116
+ return this.getDefaultCalendar().createEvent(...args);
117
+ }
118
+ createEventFromDescription(...args) {
119
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'createEventFromDescription');
120
+ return this.getDefaultCalendar().createEventFromDescription(...args);
121
+ }
122
+ createEventSeries(...args) {
123
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'createEventSeries');
124
+ return this.getDefaultCalendar().createEventSeries(...args);
125
+ }
126
+
127
+ getEventById(id) {
128
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getEventById');
129
+ return this.getDefaultCalendar().getEventById(id);
130
+ }
131
+ getEvents(...args) {
132
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getEvents');
133
+ return this.getDefaultCalendar().getEvents(...args);
134
+ }
135
+ getEventsForDay(...args) {
136
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getEventsForDay');
137
+ return this.getDefaultCalendar().getEventsForDay(...args);
138
+ }
139
+ getEventSeriesById(id) {
140
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getEventSeriesById');
141
+ return this.getDefaultCalendar().getEventSeriesById(id);
142
+ }
143
+
144
+ getColor() {
145
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getColor');
146
+ return this.getDefaultCalendar().getColor();
147
+ }
148
+ setColor(color) {
149
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'setColor');
150
+ return this.getDefaultCalendar().setColor(color);
151
+ }
152
+ getDescription() {
153
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getDescription');
154
+ return this.getDefaultCalendar().getDescription();
155
+ }
156
+ setDescription(description) {
157
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'setDescription');
158
+ return this.getDefaultCalendar().setDescription(description);
159
+ }
160
+ getId() {
161
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getId');
162
+ return this.getDefaultCalendar().getId();
163
+ }
164
+ getName() {
165
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getName');
166
+ return this.getDefaultCalendar().getName();
167
+ }
168
+ setName(name) {
169
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'setName');
170
+ return this.getDefaultCalendar().setName(name);
171
+ }
172
+ getTimeZone() {
173
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'getTimeZone');
174
+ return this.getDefaultCalendar().getTimeZone();
175
+ }
176
+ setTimeZone(timeZone) {
177
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'setTimeZone');
178
+ return this.getDefaultCalendar().setTimeZone(timeZone);
179
+ }
180
+ isHidden() {
181
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'isHidden');
182
+ return this.getDefaultCalendar().isHidden();
183
+ }
184
+ setHidden(hidden) {
185
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'setHidden');
186
+ return this.getDefaultCalendar().setHidden(hidden);
187
+ }
188
+ isSelected() {
189
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'isSelected');
190
+ return this.getDefaultCalendar().isSelected();
191
+ }
192
+ setSelected(selected) {
193
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'setSelected');
194
+ return this.getDefaultCalendar().setSelected(selected);
195
+ }
196
+ isMyPrimaryCalendar() {
197
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'isMyPrimaryCalendar');
198
+ return this.getDefaultCalendar().isMyPrimaryCalendar();
199
+ }
200
+ isOwnedByMe() {
201
+ ScriptApp.__behavior.checkMethod('CalendarApp', 'isOwnedByMe');
202
+ return this.getDefaultCalendar().isOwnedByMe();
203
+ }
204
+
205
+ // --- Internals ---
206
+
75
207
  __isCalendarAccessible(calendarId) {
76
208
  try {
77
209
  this.__checkCalendarAccess(calendarId);