@yolk-sdk/emulators 0.1.0-canary.96
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/LICENSE +21 -0
- package/README.md +2135 -0
- package/dist/anthropic.d.mts +269 -0
- package/dist/anthropic.d.mts.map +1 -0
- package/dist/anthropic.mjs +177 -0
- package/dist/anthropic.mjs.map +1 -0
- package/dist/chat-completions.d.mts +288 -0
- package/dist/chat-completions.d.mts.map +1 -0
- package/dist/chat-completions.mjs +451 -0
- package/dist/chat-completions.mjs.map +1 -0
- package/dist/codex.d.mts +265 -0
- package/dist/codex.d.mts.map +1 -0
- package/dist/codex.mjs +199 -0
- package/dist/codex.mjs.map +1 -0
- package/dist/dropbox/api.d.mts +63 -0
- package/dist/dropbox/api.d.mts.map +1 -0
- package/dist/dropbox/api.mjs +565 -0
- package/dist/dropbox/api.mjs.map +1 -0
- package/dist/dropbox/state.d.mts +136 -0
- package/dist/dropbox/state.d.mts.map +1 -0
- package/dist/dropbox/state.mjs +209 -0
- package/dist/dropbox/state.mjs.map +1 -0
- package/dist/dropbox.d.mts +79 -0
- package/dist/dropbox.d.mts.map +1 -0
- package/dist/dropbox.mjs +124 -0
- package/dist/dropbox.mjs.map +1 -0
- package/dist/email-fixtures.d.mts +36 -0
- package/dist/email-fixtures.d.mts.map +1 -0
- package/dist/email-fixtures.mjs +1080 -0
- package/dist/email-fixtures.mjs.map +1 -0
- package/dist/email.d.mts +160 -0
- package/dist/email.d.mts.map +1 -0
- package/dist/email.mjs +608 -0
- package/dist/email.mjs.map +1 -0
- package/dist/emulator-compose.d.mts +40 -0
- package/dist/emulator-compose.d.mts.map +1 -0
- package/dist/emulator-compose.mjs +91 -0
- package/dist/emulator-compose.mjs.map +1 -0
- package/dist/emulator-http.d.mts +53 -0
- package/dist/emulator-http.d.mts.map +1 -0
- package/dist/emulator-http.mjs +117 -0
- package/dist/emulator-http.mjs.map +1 -0
- package/dist/emulator-kernel.d.mts +176 -0
- package/dist/emulator-kernel.d.mts.map +1 -0
- package/dist/emulator-kernel.mjs +413 -0
- package/dist/emulator-kernel.mjs.map +1 -0
- package/dist/fixture-route.d.mts +129 -0
- package/dist/fixture-route.d.mts.map +1 -0
- package/dist/fixture-route.mjs +257 -0
- package/dist/fixture-route.mjs.map +1 -0
- package/dist/fortnox/api.d.mts +92 -0
- package/dist/fortnox/api.d.mts.map +1 -0
- package/dist/fortnox/api.mjs +750 -0
- package/dist/fortnox/api.mjs.map +1 -0
- package/dist/fortnox/state.d.mts +533 -0
- package/dist/fortnox/state.d.mts.map +1 -0
- package/dist/fortnox/state.mjs +612 -0
- package/dist/fortnox/state.mjs.map +1 -0
- package/dist/fortnox.d.mts +128 -0
- package/dist/fortnox.d.mts.map +1 -0
- package/dist/fortnox.mjs +403 -0
- package/dist/fortnox.mjs.map +1 -0
- package/dist/gateway-evaluate-recordings.d.mts +12 -0
- package/dist/gateway-evaluate-recordings.d.mts.map +1 -0
- package/dist/gateway-evaluate-recordings.mjs +128 -0
- package/dist/gateway-evaluate-recordings.mjs.map +1 -0
- package/dist/gateway.d.mts +272 -0
- package/dist/gateway.d.mts.map +1 -0
- package/dist/gateway.mjs +400 -0
- package/dist/gateway.mjs.map +1 -0
- package/dist/github/api.d.mts +63 -0
- package/dist/github/api.d.mts.map +1 -0
- package/dist/github/api.mjs +527 -0
- package/dist/github/api.mjs.map +1 -0
- package/dist/github/state.d.mts +272 -0
- package/dist/github/state.d.mts.map +1 -0
- package/dist/github/state.mjs +303 -0
- package/dist/github/state.mjs.map +1 -0
- package/dist/github.d.mts +77 -0
- package/dist/github.d.mts.map +1 -0
- package/dist/github.mjs +180 -0
- package/dist/github.mjs.map +1 -0
- package/dist/google/calendar.d.mts +10 -0
- package/dist/google/calendar.d.mts.map +1 -0
- package/dist/google/calendar.mjs +252 -0
- package/dist/google/calendar.mjs.map +1 -0
- package/dist/google/drive.d.mts +8 -0
- package/dist/google/drive.d.mts.map +1 -0
- package/dist/google/drive.mjs +252 -0
- package/dist/google/drive.mjs.map +1 -0
- package/dist/google/gmail.d.mts +10 -0
- package/dist/google/gmail.d.mts.map +1 -0
- package/dist/google/gmail.mjs +591 -0
- package/dist/google/gmail.mjs.map +1 -0
- package/dist/google/shared.d.mts +107 -0
- package/dist/google/shared.d.mts.map +1 -0
- package/dist/google/shared.mjs +146 -0
- package/dist/google/shared.mjs.map +1 -0
- package/dist/google/state.d.mts +385 -0
- package/dist/google/state.d.mts.map +1 -0
- package/dist/google/state.mjs +496 -0
- package/dist/google/state.mjs.map +1 -0
- package/dist/google.d.mts +75 -0
- package/dist/google.d.mts.map +1 -0
- package/dist/google.mjs +210 -0
- package/dist/google.mjs.map +1 -0
- package/dist/linkedin-search/api.d.mts +45 -0
- package/dist/linkedin-search/api.d.mts.map +1 -0
- package/dist/linkedin-search/api.mjs +181 -0
- package/dist/linkedin-search/api.mjs.map +1 -0
- package/dist/linkedin-search/state.d.mts +123 -0
- package/dist/linkedin-search/state.d.mts.map +1 -0
- package/dist/linkedin-search/state.mjs +261 -0
- package/dist/linkedin-search/state.mjs.map +1 -0
- package/dist/linkedin-search.d.mts +69 -0
- package/dist/linkedin-search.d.mts.map +1 -0
- package/dist/linkedin-search.mjs +158 -0
- package/dist/linkedin-search.mjs.map +1 -0
- package/dist/mcp/api.d.mts +44 -0
- package/dist/mcp/api.d.mts.map +1 -0
- package/dist/mcp/api.mjs +557 -0
- package/dist/mcp/api.mjs.map +1 -0
- package/dist/mcp/recordings.d.mts +40 -0
- package/dist/mcp/recordings.d.mts.map +1 -0
- package/dist/mcp/recordings.mjs +2390 -0
- package/dist/mcp/recordings.mjs.map +1 -0
- package/dist/mcp/state.d.mts +71 -0
- package/dist/mcp/state.d.mts.map +1 -0
- package/dist/mcp/state.mjs +83 -0
- package/dist/mcp/state.mjs.map +1 -0
- package/dist/mcp.d.mts +84 -0
- package/dist/mcp.d.mts.map +1 -0
- package/dist/mcp.mjs +241 -0
- package/dist/mcp.mjs.map +1 -0
- package/dist/messages.d.mts +183 -0
- package/dist/messages.d.mts.map +1 -0
- package/dist/messages.mjs +532 -0
- package/dist/messages.mjs.map +1 -0
- package/dist/microsoft/api.d.mts +47 -0
- package/dist/microsoft/api.d.mts.map +1 -0
- package/dist/microsoft/api.mjs +178 -0
- package/dist/microsoft/api.mjs.map +1 -0
- package/dist/microsoft/calendar.d.mts +30 -0
- package/dist/microsoft/calendar.d.mts.map +1 -0
- package/dist/microsoft/calendar.mjs +271 -0
- package/dist/microsoft/calendar.mjs.map +1 -0
- package/dist/microsoft/drive.d.mts +36 -0
- package/dist/microsoft/drive.d.mts.map +1 -0
- package/dist/microsoft/drive.mjs +298 -0
- package/dist/microsoft/drive.mjs.map +1 -0
- package/dist/microsoft/graph.d.mts +198 -0
- package/dist/microsoft/graph.d.mts.map +1 -0
- package/dist/microsoft/graph.mjs +264 -0
- package/dist/microsoft/graph.mjs.map +1 -0
- package/dist/microsoft/mail.d.mts +48 -0
- package/dist/microsoft/mail.d.mts.map +1 -0
- package/dist/microsoft/mail.mjs +488 -0
- package/dist/microsoft/mail.mjs.map +1 -0
- package/dist/microsoft/state.d.mts +480 -0
- package/dist/microsoft/state.d.mts.map +1 -0
- package/dist/microsoft/state.mjs +625 -0
- package/dist/microsoft/state.mjs.map +1 -0
- package/dist/microsoft.d.mts +168 -0
- package/dist/microsoft.d.mts.map +1 -0
- package/dist/microsoft.mjs +483 -0
- package/dist/microsoft.mjs.map +1 -0
- package/dist/node.d.mts +47 -0
- package/dist/node.d.mts.map +1 -0
- package/dist/node.mjs +178 -0
- package/dist/node.mjs.map +1 -0
- package/dist/notion/api.d.mts +51 -0
- package/dist/notion/api.d.mts.map +1 -0
- package/dist/notion/api.mjs +507 -0
- package/dist/notion/api.mjs.map +1 -0
- package/dist/notion/state.d.mts +329 -0
- package/dist/notion/state.d.mts.map +1 -0
- package/dist/notion/state.mjs +432 -0
- package/dist/notion/state.mjs.map +1 -0
- package/dist/notion.d.mts +72 -0
- package/dist/notion.d.mts.map +1 -0
- package/dist/notion.mjs +127 -0
- package/dist/notion.mjs.map +1 -0
- package/dist/openai.d.mts +165 -0
- package/dist/openai.d.mts.map +1 -0
- package/dist/openai.mjs +141 -0
- package/dist/openai.mjs.map +1 -0
- package/dist/opencode-recordings.d.mts +7 -0
- package/dist/opencode-recordings.d.mts.map +1 -0
- package/dist/opencode-recordings.mjs +222 -0
- package/dist/opencode-recordings.mjs.map +1 -0
- package/dist/opencode.d.mts +70 -0
- package/dist/opencode.d.mts.map +1 -0
- package/dist/opencode.mjs +217 -0
- package/dist/opencode.mjs.map +1 -0
- package/dist/r2-fixtures.d.mts +36 -0
- package/dist/r2-fixtures.d.mts.map +1 -0
- package/dist/r2-fixtures.mjs +245 -0
- package/dist/r2-fixtures.mjs.map +1 -0
- package/dist/r2-guard.d.mts +43 -0
- package/dist/r2-guard.d.mts.map +1 -0
- package/dist/r2-guard.mjs +215 -0
- package/dist/r2-guard.mjs.map +1 -0
- package/dist/r2.d.mts +166 -0
- package/dist/r2.d.mts.map +1 -0
- package/dist/r2.mjs +517 -0
- package/dist/r2.mjs.map +1 -0
- package/dist/responses.d.mts +231 -0
- package/dist/responses.d.mts.map +1 -0
- package/dist/responses.mjs +556 -0
- package/dist/responses.mjs.map +1 -0
- package/dist/route-evidence.d.mts +45 -0
- package/dist/route-evidence.d.mts.map +1 -0
- package/dist/route-evidence.mjs +56 -0
- package/dist/route-evidence.mjs.map +1 -0
- package/dist/router.d.mts +93 -0
- package/dist/router.d.mts.map +1 -0
- package/dist/router.mjs +259 -0
- package/dist/router.mjs.map +1 -0
- package/dist/stateful-core.d.mts +17 -0
- package/dist/stateful-core.d.mts.map +1 -0
- package/dist/stateful-core.mjs +41 -0
- package/dist/stateful-core.mjs.map +1 -0
- package/dist/stateful-emulator.d.mts +565 -0
- package/dist/stateful-emulator.d.mts.map +1 -0
- package/dist/stateful-emulator.mjs +1228 -0
- package/dist/stateful-emulator.mjs.map +1 -0
- package/dist/stateful-secrets.d.mts +84 -0
- package/dist/stateful-secrets.d.mts.map +1 -0
- package/dist/stateful-secrets.mjs +216 -0
- package/dist/stateful-secrets.mjs.map +1 -0
- package/dist/subscription-usage-recordings.d.mts +9 -0
- package/dist/subscription-usage-recordings.d.mts.map +1 -0
- package/dist/subscription-usage-recordings.mjs +53 -0
- package/dist/subscription-usage-recordings.mjs.map +1 -0
- package/dist/subscription-usage.d.mts +53 -0
- package/dist/subscription-usage.d.mts.map +1 -0
- package/dist/subscription-usage.mjs +21 -0
- package/dist/subscription-usage.mjs.map +1 -0
- package/dist/telegram/api.d.mts +34 -0
- package/dist/telegram/api.d.mts.map +1 -0
- package/dist/telegram/api.mjs +257 -0
- package/dist/telegram/api.mjs.map +1 -0
- package/dist/telegram/state.d.mts +128 -0
- package/dist/telegram/state.d.mts.map +1 -0
- package/dist/telegram/state.mjs +154 -0
- package/dist/telegram/state.mjs.map +1 -0
- package/dist/telegram.d.mts +62 -0
- package/dist/telegram.d.mts.map +1 -0
- package/dist/telegram.mjs +127 -0
- package/dist/telegram.mjs.map +1 -0
- package/dist/todoist/api.d.mts +55 -0
- package/dist/todoist/api.d.mts.map +1 -0
- package/dist/todoist/api.mjs +415 -0
- package/dist/todoist/api.mjs.map +1 -0
- package/dist/todoist/state.d.mts +275 -0
- package/dist/todoist/state.d.mts.map +1 -0
- package/dist/todoist/state.mjs +345 -0
- package/dist/todoist/state.mjs.map +1 -0
- package/dist/todoist.d.mts +68 -0
- package/dist/todoist.d.mts.map +1 -0
- package/dist/todoist.mjs +181 -0
- package/dist/todoist.mjs.map +1 -0
- package/dist/xai.d.mts +267 -0
- package/dist/xai.d.mts.map +1 -0
- package/dist/xai.mjs +252 -0
- package/dist/xai.mjs.map +1 -0
- package/package.json +157 -0
- package/src/anthropic.ts +289 -0
- package/src/chat-completions.ts +924 -0
- package/src/codex.ts +296 -0
- package/src/dropbox/api.ts +1084 -0
- package/src/dropbox/state.ts +364 -0
- package/src/dropbox.ts +203 -0
- package/src/email-fixtures.ts +1297 -0
- package/src/email.ts +1108 -0
- package/src/emulator-compose.ts +147 -0
- package/src/emulator-http.ts +184 -0
- package/src/emulator-kernel.ts +844 -0
- package/src/fixture-route.ts +561 -0
- package/src/fortnox/api.ts +1352 -0
- package/src/fortnox/state.ts +798 -0
- package/src/fortnox.ts +801 -0
- package/src/gateway-evaluate-recordings.ts +153 -0
- package/src/gateway.ts +530 -0
- package/src/github/api.ts +986 -0
- package/src/github/state.ts +439 -0
- package/src/github.ts +271 -0
- package/src/google/calendar.ts +486 -0
- package/src/google/drive.ts +471 -0
- package/src/google/gmail.ts +1011 -0
- package/src/google/shared.ts +321 -0
- package/src/google/state.ts +686 -0
- package/src/google.ts +298 -0
- package/src/linkedin-search/api.ts +363 -0
- package/src/linkedin-search/state.ts +373 -0
- package/src/linkedin-search.ts +241 -0
- package/src/mcp/api.ts +995 -0
- package/src/mcp/recordings.ts +2665 -0
- package/src/mcp/state.ts +125 -0
- package/src/mcp.ts +319 -0
- package/src/messages.ts +844 -0
- package/src/microsoft/api.ts +426 -0
- package/src/microsoft/calendar.ts +491 -0
- package/src/microsoft/drive.ts +541 -0
- package/src/microsoft/graph.ts +550 -0
- package/src/microsoft/mail.ts +833 -0
- package/src/microsoft/state.ts +822 -0
- package/src/microsoft.ts +982 -0
- package/src/node.ts +293 -0
- package/src/notion/api.ts +976 -0
- package/src/notion/state.ts +512 -0
- package/src/notion.ts +210 -0
- package/src/openai.ts +198 -0
- package/src/opencode-recordings.ts +257 -0
- package/src/opencode.ts +271 -0
- package/src/r2-fixtures.ts +295 -0
- package/src/r2-guard.ts +323 -0
- package/src/r2.ts +890 -0
- package/src/responses.ts +901 -0
- package/src/route-evidence.ts +90 -0
- package/src/router.ts +490 -0
- package/src/stateful-core.ts +70 -0
- package/src/stateful-emulator.ts +2571 -0
- package/src/stateful-secrets.ts +299 -0
- package/src/subscription-usage-recordings.ts +77 -0
- package/src/subscription-usage.ts +68 -0
- package/src/telegram/api.ts +437 -0
- package/src/telegram/state.ts +229 -0
- package/src/telegram.ts +227 -0
- package/src/todoist/api.ts +736 -0
- package/src/todoist/state.ts +456 -0
- package/src/todoist.ts +316 -0
- package/src/xai.ts +341 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { bodyObject, codes, collection, emptyResponse, entity, invalidValue, isJsonObject, jsonResponse, metadataContext, nestedObject, notEmulated, nowTimestamp, odataKey, padded, personalSite, project, selectSuffix, selectedFields, singlePage } from "./graph.mjs";
|
|
2
|
+
import { Predicate } from "effect";
|
|
3
|
+
//#region src/microsoft/drive.ts
|
|
4
|
+
/**
|
|
5
|
+
* Microsoft Graph emulator OneDrive routes (internal): item read, children listing, folder
|
|
6
|
+
* create, delete, and the asynchronous copy with its monitor URL. Wire shapes follow the
|
|
7
|
+
* synthetic OneDrive conformance fixtures.
|
|
8
|
+
*
|
|
9
|
+
* Deleted items are removed with their subtree (no recycle bin is emulated). Folder create and
|
|
10
|
+
* copy take `@microsoft.graph.conflictBehavior` `fail` only, as the fixtures send it, and only for
|
|
11
|
+
* a name that is free: no fixture records a name conflict, so one fails closed (400). Children
|
|
12
|
+
* listings need `$top` and answer one page (no `$skip`, no `@odata.nextLink`), as the fixtures
|
|
13
|
+
* record them. A copy takes what the copy fixture sends (a file, `parentReference { driveId, id }`
|
|
14
|
+
* on the same drive, no new `name`; anything else is not emulated) and answers 202 with one
|
|
15
|
+
* monitor `Location` on the SharePoint origin; the monitor needs no credentials, answers `inProgress` (202) for `copyInProgressPolls` polls (default 0), then runs
|
|
16
|
+
* the copy and answers `completed` (200) with the new item's `resourceId`, as the fixture's first
|
|
17
|
+
* poll does. No fixture records a failed copy: when the copy can no longer run (source or
|
|
18
|
+
* destination gone, or the name taken), the monitor answers 400 not emulated.
|
|
19
|
+
*
|
|
20
|
+
* @experimental
|
|
21
|
+
*/
|
|
22
|
+
const itemFields = [
|
|
23
|
+
"id",
|
|
24
|
+
"name",
|
|
25
|
+
"size",
|
|
26
|
+
"webUrl",
|
|
27
|
+
"createdDateTime",
|
|
28
|
+
"lastModifiedDateTime",
|
|
29
|
+
"eTag",
|
|
30
|
+
"cTag",
|
|
31
|
+
"parentReference",
|
|
32
|
+
"file",
|
|
33
|
+
"folder",
|
|
34
|
+
"package",
|
|
35
|
+
"remoteItem",
|
|
36
|
+
"shared",
|
|
37
|
+
"deleted"
|
|
38
|
+
];
|
|
39
|
+
const notFound = (request) => request.error(404, codes.driveItemNotFound, "The resource could not be found.");
|
|
40
|
+
const childrenOf = (state, id) => state.driveItems.filter((item) => item.parentId === id);
|
|
41
|
+
/** The item and its descendants. */
|
|
42
|
+
const subtreeOf = (state, root) => [root, ...childrenOf(state, root.id).flatMap((child) => subtreeOf(state, child))];
|
|
43
|
+
/** Folders report the size of their contents. */
|
|
44
|
+
const sizeOf = (state, item) => item.kind === "file" ? item.size : childrenOf(state, item.id).reduce((sum, child) => sum + sizeOf(state, child), 0);
|
|
45
|
+
/** Names from below the root down to the item (empty for the root). */
|
|
46
|
+
const pathNames = (state, item) => {
|
|
47
|
+
const parent = item.parentId === null ? void 0 : state.driveItems.find((candidate) => candidate.id === item.parentId);
|
|
48
|
+
return parent === void 0 ? [] : [...pathNames(state, parent), item.name];
|
|
49
|
+
};
|
|
50
|
+
/** A stable synthetic GUID for an item's `eTag`/`cTag` (FNV-1a over the id). */
|
|
51
|
+
const itemGuid = (id) => {
|
|
52
|
+
let hash = 2166136261;
|
|
53
|
+
for (const character of id) hash = Math.imul(hash ^ (character.codePointAt(0) ?? 0), 16777619) >>> 0;
|
|
54
|
+
return `${hash.toString(16).padStart(8, "0")}-0000-4000-8000-000000000000`;
|
|
55
|
+
};
|
|
56
|
+
/** The full item, in the fixture key order. */
|
|
57
|
+
const renderItem = (state, env, item) => {
|
|
58
|
+
const site = personalSite(state.user);
|
|
59
|
+
const names = pathNames(state, item);
|
|
60
|
+
const parentPath = names.slice(0, -1);
|
|
61
|
+
const parentReference = item.parentId === null ? {
|
|
62
|
+
driveType: state.drive.driveType,
|
|
63
|
+
driveId: state.drive.id
|
|
64
|
+
} : {
|
|
65
|
+
driveType: state.drive.driveType,
|
|
66
|
+
driveId: state.drive.id,
|
|
67
|
+
id: item.parentId,
|
|
68
|
+
path: `/drive/root:${parentPath.map((name) => `/${name}`).join("")}`
|
|
69
|
+
};
|
|
70
|
+
const base = {
|
|
71
|
+
id: item.id,
|
|
72
|
+
name: item.name,
|
|
73
|
+
size: sizeOf(state, item),
|
|
74
|
+
webUrl: `${env.sharePointOrigin}/personal/${site}/Documents${names.map((name) => `/${encodeURIComponent(name)}`).join("")}`,
|
|
75
|
+
createdDateTime: item.createdDateTime,
|
|
76
|
+
lastModifiedDateTime: item.lastModifiedDateTime,
|
|
77
|
+
eTag: `"{${itemGuid(item.id)}},1"`,
|
|
78
|
+
cTag: `"c:{${itemGuid(item.id)}},0"`,
|
|
79
|
+
parentReference
|
|
80
|
+
};
|
|
81
|
+
if (item.kind === "folder") return {
|
|
82
|
+
...base,
|
|
83
|
+
folder: { childCount: childrenOf(state, item.id).length }
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
...base,
|
|
87
|
+
file: {
|
|
88
|
+
mimeType: item.mimeType ?? "application/octet-stream",
|
|
89
|
+
hashes: item.quickXorHash === null ? {} : { quickXorHash: item.quickXorHash }
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/** `drives('{driveId}')`, the context prefix of every OneDrive route. */
|
|
94
|
+
const driveContext = (request) => `drives${odataKey(request.params.driveId ?? "")}`;
|
|
95
|
+
/** `drives('{driveId}')/items('{itemId}')/children`. */
|
|
96
|
+
const childrenContext = (request) => `${driveContext(request)}/items${odataKey(request.params.itemId ?? "")}/children`;
|
|
97
|
+
/** The drive of the `{driveId}` segment, or 404. */
|
|
98
|
+
const driveProblem = (state, request) => request.params.driveId === state.drive.id ? void 0 : notFound(request);
|
|
99
|
+
/** The item of an id segment (`root` names the drive root). */
|
|
100
|
+
const findItem = (state, id) => {
|
|
101
|
+
const resolved = id.toLowerCase() === "root" ? state.drive.rootId : id;
|
|
102
|
+
return state.driveItems.find((item) => item.id === resolved);
|
|
103
|
+
};
|
|
104
|
+
/** `GET /drives/{driveId}/items/{itemId}` (`$select`). */
|
|
105
|
+
const getItem = (state, request, env) => {
|
|
106
|
+
const drive = driveProblem(state, request);
|
|
107
|
+
if (drive !== void 0) return drive;
|
|
108
|
+
const fields = selectedFields(request, itemFields);
|
|
109
|
+
if (fields instanceof Response) return fields;
|
|
110
|
+
const item = findItem(state, request.params.itemId ?? "");
|
|
111
|
+
const context = metadataContext(env, `${driveContext(request)}/items${selectSuffix(fields)}/$entity`);
|
|
112
|
+
return item === void 0 ? notFound(request) : jsonResponse(200, entity(context, project(renderItem(state, env, item), fields)));
|
|
113
|
+
};
|
|
114
|
+
const byName = (left, right) => left.name.localeCompare(right.name, "en", { sensitivity: "base" }) || left.id.localeCompare(right.id);
|
|
115
|
+
/** The largest children `$top` a fixture sends (`$top=200`, create-folder and copy cases). */
|
|
116
|
+
const childrenMaxTop = 200;
|
|
117
|
+
/**
|
|
118
|
+
* `GET /drives/{driveId}/items/{itemId}/children`: by name, at most `$top` (one page only;
|
|
119
|
+
* `$top` required, at most `childrenMaxTop`).
|
|
120
|
+
*/
|
|
121
|
+
const listChildren = (state, request, env) => {
|
|
122
|
+
const drive = driveProblem(state, request);
|
|
123
|
+
if (drive !== void 0) return drive;
|
|
124
|
+
const fields = selectedFields(request, itemFields);
|
|
125
|
+
if (fields instanceof Response) return fields;
|
|
126
|
+
const parent = findItem(state, request.params.itemId ?? "");
|
|
127
|
+
if (parent === void 0) return notFound(request);
|
|
128
|
+
if (parent.kind !== "folder") return notEmulated(request, "children of a file are not emulated.");
|
|
129
|
+
const page = singlePage([...childrenOf(state, parent.id)].sort(byName), request, childrenMaxTop);
|
|
130
|
+
if (page instanceof Response) return page;
|
|
131
|
+
return jsonResponse(200, collection(metadataContext(env, `${childrenContext(request)}${selectSuffix(fields)}`), page.map((item) => project(renderItem(state, env, item), fields)), void 0));
|
|
132
|
+
};
|
|
133
|
+
const invalidNamePattern = /["*:<>?/\\|]/;
|
|
134
|
+
const nameProblem = (request, name) => Predicate.isString(name) && name.trim() !== "" && name.trim() === name && !invalidNamePattern.test(name) && name !== "." && name !== ".." && !name.endsWith(".") ? name : invalidValue(request, "the item name is empty or has characters OneDrive does not allow.");
|
|
135
|
+
const nameTaken = (state, parentId, name) => childrenOf(state, parentId).some((child) => child.name.toLowerCase() === name.toLowerCase());
|
|
136
|
+
const nextItemId = (state) => {
|
|
137
|
+
let number = state.counters.nextItemNumber;
|
|
138
|
+
let id = `01SYNTHETICITEM${padded(number, 17)}`;
|
|
139
|
+
while (state.driveItems.some((item) => item.id === id)) {
|
|
140
|
+
number += 1;
|
|
141
|
+
id = `01SYNTHETICITEM${padded(number, 17)}`;
|
|
142
|
+
}
|
|
143
|
+
state.counters = {
|
|
144
|
+
...state.counters,
|
|
145
|
+
nextItemNumber: number + 1
|
|
146
|
+
};
|
|
147
|
+
return id;
|
|
148
|
+
};
|
|
149
|
+
const nameConflict = (request) => notEmulated(request, "name conflicts are not emulated (the name is already taken).");
|
|
150
|
+
/** Only `fail`, as the fixtures send it; anything else (or none) is not emulated. */
|
|
151
|
+
const conflictBehaviorProblem = (request, value) => value === "fail" ? void 0 : notEmulated(request, "@microsoft.graph.conflictBehavior must be fail (other or missing values are not emulated).");
|
|
152
|
+
/**
|
|
153
|
+
* `POST /drives/{driveId}/items/{itemId}/children` with a `folder` facet and
|
|
154
|
+
* `@microsoft.graph.conflictBehavior: fail`: 201 with the folder. A taken name, and creating
|
|
155
|
+
* files here, are not emulated.
|
|
156
|
+
*/
|
|
157
|
+
const createFolder = (state, request, env) => {
|
|
158
|
+
const drive = driveProblem(state, request);
|
|
159
|
+
if (drive !== void 0) return drive;
|
|
160
|
+
const parent = findItem(state, request.params.itemId ?? "");
|
|
161
|
+
if (parent === void 0) return notFound(request);
|
|
162
|
+
if (parent.kind !== "folder") return invalidValue(request, "the parent item is not a folder.");
|
|
163
|
+
const fields = bodyObject(request, [
|
|
164
|
+
"name",
|
|
165
|
+
"folder",
|
|
166
|
+
"@microsoft.graph.conflictBehavior"
|
|
167
|
+
]);
|
|
168
|
+
if (fields instanceof Response) return fields;
|
|
169
|
+
if (fields.folder === void 0) return notEmulated(request, "only folder creation (a folder facet) is emulated here.");
|
|
170
|
+
const folderFacet = fields.folder;
|
|
171
|
+
if (!isJsonObject(folderFacet) || Object.keys(folderFacet).length > 0) return notEmulated(request, "the folder facet must be {}.");
|
|
172
|
+
const name = nameProblem(request, fields.name);
|
|
173
|
+
if (name instanceof Response) return name;
|
|
174
|
+
const behavior = conflictBehaviorProblem(request, fields["@microsoft.graph.conflictBehavior"]);
|
|
175
|
+
if (behavior !== void 0) return behavior;
|
|
176
|
+
if (nameTaken(state, parent.id, name)) return nameConflict(request);
|
|
177
|
+
const now = nowTimestamp(env);
|
|
178
|
+
const folder = {
|
|
179
|
+
id: nextItemId(state),
|
|
180
|
+
parentId: parent.id,
|
|
181
|
+
name,
|
|
182
|
+
kind: "folder",
|
|
183
|
+
size: 0,
|
|
184
|
+
mimeType: null,
|
|
185
|
+
quickXorHash: null,
|
|
186
|
+
createdDateTime: now,
|
|
187
|
+
lastModifiedDateTime: now
|
|
188
|
+
};
|
|
189
|
+
state.driveItems = [...state.driveItems, folder];
|
|
190
|
+
return jsonResponse(201, entity(metadataContext(env, `${childrenContext(request)}/$entity`), renderItem(state, env, folder)));
|
|
191
|
+
};
|
|
192
|
+
/** `DELETE /drives/{driveId}/items/{itemId}`: 204; the item and its subtree are removed. */
|
|
193
|
+
const deleteItem = (state, request) => {
|
|
194
|
+
const drive = driveProblem(state, request);
|
|
195
|
+
if (drive !== void 0) return drive;
|
|
196
|
+
const item = findItem(state, request.params.itemId ?? "");
|
|
197
|
+
if (item === void 0) return notFound(request);
|
|
198
|
+
if (item.parentId === null) return notEmulated(request, "deleting the drive root is not emulated.");
|
|
199
|
+
const removed = new Set(subtreeOf(state, item).map((entry) => entry.id));
|
|
200
|
+
state.driveItems = state.driveItems.filter((entry) => !removed.has(entry.id));
|
|
201
|
+
return emptyResponse(204);
|
|
202
|
+
};
|
|
203
|
+
const monitorPath = (state, id) => `/personal/${personalSite(state.user)}/_api/v2.0/monitor/${id}`;
|
|
204
|
+
/**
|
|
205
|
+
* `POST /drives/{driveId}/items/{itemId}/copy` (`{ parentReference: { driveId, id } }`, query
|
|
206
|
+
* `@microsoft.graph.conflictBehavior=fail`): 202 with an empty body and one monitor `Location`.
|
|
207
|
+
* The copy itself runs when the monitor reports completion. Only what the copy fixture sends is
|
|
208
|
+
* emulated: a file source, a destination on the same drive named by `driveId` and `id`, and the
|
|
209
|
+
* source's name (a new `name`, a folder source, or a missing `driveId` fail closed).
|
|
210
|
+
*/
|
|
211
|
+
const copyItem = (state, request, env) => {
|
|
212
|
+
const drive = driveProblem(state, request);
|
|
213
|
+
if (drive !== void 0) return drive;
|
|
214
|
+
const source = findItem(state, request.params.itemId ?? "");
|
|
215
|
+
if (source === void 0) return notFound(request);
|
|
216
|
+
if (source.kind !== "file") return notEmulated(request, "copying a folder is not emulated (the fixture copies a file).");
|
|
217
|
+
const behavior = conflictBehaviorProblem(request, request.query.get("@microsoft.graph.conflictBehavior") ?? void 0);
|
|
218
|
+
if (behavior !== void 0) return behavior;
|
|
219
|
+
const fields = bodyObject(request, ["parentReference"]);
|
|
220
|
+
if (fields instanceof Response) return fields;
|
|
221
|
+
const reference = nestedObject(request, fields.parentReference, ["driveId", "id"], "parentReference { driveId, id }");
|
|
222
|
+
if (reference instanceof Response) return reference;
|
|
223
|
+
if (reference.driveId === void 0) return notEmulated(request, "copies without parentReference.driveId are not emulated.");
|
|
224
|
+
if (!Predicate.isString(reference.id) || !Predicate.isString(reference.driveId)) return invalidValue(request, "parentReference must be { driveId, id } with string values.");
|
|
225
|
+
if (reference.driveId !== state.drive.id) return notEmulated(request, "copies to another drive are not emulated.");
|
|
226
|
+
const destination = findItem(state, reference.id);
|
|
227
|
+
if (destination === void 0) return notFound(request);
|
|
228
|
+
if (destination.kind !== "folder") return invalidValue(request, "the destination is not a folder.");
|
|
229
|
+
const number = env.monitorCounter.next;
|
|
230
|
+
env.monitorCounter.next += 1;
|
|
231
|
+
const monitor = {
|
|
232
|
+
id: `00000000-0000-4000-8000-${padded(number, 12)}`,
|
|
233
|
+
sourceId: source.id,
|
|
234
|
+
destinationParentId: destination.id,
|
|
235
|
+
pollsLeft: env.copyInProgressPolls,
|
|
236
|
+
resourceId: void 0
|
|
237
|
+
};
|
|
238
|
+
env.monitors.set(monitor.id, monitor);
|
|
239
|
+
return emptyResponse(202, { location: `${env.sharePointOrigin}${monitorPath(state, monitor.id)}` });
|
|
240
|
+
};
|
|
241
|
+
/** Copy the file `item` under `parentId` with a fresh id and its name; returns the new id. */
|
|
242
|
+
const copyFile = (state, env, item, parentId) => {
|
|
243
|
+
const now = nowTimestamp(env);
|
|
244
|
+
const copy = {
|
|
245
|
+
...item,
|
|
246
|
+
id: nextItemId(state),
|
|
247
|
+
parentId,
|
|
248
|
+
createdDateTime: now,
|
|
249
|
+
lastModifiedDateTime: now
|
|
250
|
+
};
|
|
251
|
+
state.driveItems = [...state.driveItems, copy];
|
|
252
|
+
return copy.id;
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Run a monitored copy: its `resourceId`, or why it cannot run (the source or destination is
|
|
256
|
+
* gone, or the name is taken; no fixture records a failed copy).
|
|
257
|
+
*/
|
|
258
|
+
const runCopy = (state, env, monitor) => {
|
|
259
|
+
const source = findItem(state, monitor.sourceId);
|
|
260
|
+
const destination = findItem(state, monitor.destinationParentId);
|
|
261
|
+
if (source === void 0 || destination === void 0 || destination.kind !== "folder") return { problem: "the copy source or destination no longer exists" };
|
|
262
|
+
if (nameTaken(state, destination.id, source.name)) return { problem: "the copy name is already taken in the destination" };
|
|
263
|
+
return copyFile(state, env, source, destination.id);
|
|
264
|
+
};
|
|
265
|
+
const monitorContentType = "application/json;odata.metadata=minimal;odata.streaming=true";
|
|
266
|
+
/**
|
|
267
|
+
* `GET /personal/{site}/_api/v2.0/monitor/{monitorId}` on the SharePoint origin (no credentials
|
|
268
|
+
* needed, like the real capability URL): `inProgress` (202) while polls remain, then the copy runs
|
|
269
|
+
* once and every later poll answers `completed` (200, `resourceId`). A copy that cannot run
|
|
270
|
+
* answers 400 not emulated (failed copies are not emulated) and stays pending.
|
|
271
|
+
*/
|
|
272
|
+
const copyMonitor = (state, request, env) => {
|
|
273
|
+
const monitor = env.monitors.get(request.params.monitorId ?? "");
|
|
274
|
+
if (monitor === void 0 || request.params.site !== personalSite(state.user)) return notFound(request);
|
|
275
|
+
const context = `${env.sharePointOrigin}/personal/${personalSite(state.user)}/_api/v2.0/$metadata#oneDrive.asynchronousOperationStatus`;
|
|
276
|
+
const headers = { "content-type": monitorContentType };
|
|
277
|
+
if (monitor.resourceId === void 0 && monitor.pollsLeft > 0) {
|
|
278
|
+
monitor.pollsLeft -= 1;
|
|
279
|
+
return jsonResponse(202, {
|
|
280
|
+
"@odata.context": context,
|
|
281
|
+
percentageComplete: 0,
|
|
282
|
+
status: "inProgress"
|
|
283
|
+
}, headers);
|
|
284
|
+
}
|
|
285
|
+
const resourceId = monitor.resourceId ?? runCopy(state, env, monitor);
|
|
286
|
+
if (!Predicate.isString(resourceId)) return notEmulated(request, `${resourceId.problem} (failed copies are not emulated).`);
|
|
287
|
+
monitor.resourceId = resourceId;
|
|
288
|
+
return jsonResponse(200, {
|
|
289
|
+
"@odata.context": context,
|
|
290
|
+
percentageComplete: 100,
|
|
291
|
+
resourceId,
|
|
292
|
+
status: "completed"
|
|
293
|
+
}, headers);
|
|
294
|
+
};
|
|
295
|
+
//#endregion
|
|
296
|
+
export { copyItem, copyMonitor, createFolder, deleteItem, getItem, listChildren };
|
|
297
|
+
|
|
298
|
+
//# sourceMappingURL=drive.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drive.mjs","names":[],"sources":["../../src/microsoft/drive.ts"],"sourcesContent":["/**\n * Microsoft Graph emulator OneDrive routes (internal): item read, children listing, folder\n * create, delete, and the asynchronous copy with its monitor URL. Wire shapes follow the\n * synthetic OneDrive conformance fixtures.\n *\n * Deleted items are removed with their subtree (no recycle bin is emulated). Folder create and\n * copy take `@microsoft.graph.conflictBehavior` `fail` only, as the fixtures send it, and only for\n * a name that is free: no fixture records a name conflict, so one fails closed (400). Children\n * listings need `$top` and answer one page (no `$skip`, no `@odata.nextLink`), as the fixtures\n * record them. A copy takes what the copy fixture sends (a file, `parentReference { driveId, id }`\n * on the same drive, no new `name`; anything else is not emulated) and answers 202 with one\n * monitor `Location` on the SharePoint origin; the monitor needs no credentials, answers `inProgress` (202) for `copyInProgressPolls` polls (default 0), then runs\n * the copy and answers `completed` (200) with the new item's `resourceId`, as the fixture's first\n * poll does. No fixture records a failed copy: when the copy can no longer run (source or\n * destination gone, or the name taken), the monitor answers 400 not emulated.\n *\n * @experimental\n */\nimport { Predicate } from 'effect'\nimport type * as Schema from 'effect/Schema'\nimport {\n bodyObject,\n codes,\n collection,\n emptyResponse,\n entity,\n invalidValue,\n isJsonObject,\n jsonResponse,\n metadataContext,\n nestedObject,\n notEmulated,\n nowTimestamp,\n odataKey,\n padded,\n personalSite,\n project,\n selectSuffix,\n selectedFields,\n singlePage,\n type CopyMonitor,\n type MicrosoftApiEnv,\n type RouteHandler,\n type RouteRequest\n} from './graph.ts'\nimport type { MicrosoftEmulatorDriveItem, MicrosoftEmulatorState } from './state.ts'\n\nconst itemFields: ReadonlyArray<string> = [\n 'id',\n 'name',\n 'size',\n 'webUrl',\n 'createdDateTime',\n 'lastModifiedDateTime',\n 'eTag',\n 'cTag',\n 'parentReference',\n 'file',\n 'folder',\n 'package',\n 'remoteItem',\n 'shared',\n 'deleted'\n]\n\nconst notFound = (request: RouteRequest): Response =>\n request.error(404, codes.driveItemNotFound, 'The resource could not be found.')\n\nconst childrenOf = (state: MicrosoftEmulatorState, id: string) =>\n state.driveItems.filter(item => item.parentId === id)\n\n/** The item and its descendants. */\nconst subtreeOf = (\n state: MicrosoftEmulatorState,\n root: MicrosoftEmulatorDriveItem\n): ReadonlyArray<MicrosoftEmulatorDriveItem> => [\n root,\n ...childrenOf(state, root.id).flatMap(child => subtreeOf(state, child))\n]\n\n/** Folders report the size of their contents. */\nconst sizeOf = (state: MicrosoftEmulatorState, item: MicrosoftEmulatorDriveItem): number =>\n item.kind === 'file'\n ? item.size\n : childrenOf(state, item.id).reduce((sum, child) => sum + sizeOf(state, child), 0)\n\n/** Names from below the root down to the item (empty for the root). */\nconst pathNames = (\n state: MicrosoftEmulatorState,\n item: MicrosoftEmulatorDriveItem\n): ReadonlyArray<string> => {\n const parent =\n item.parentId === null\n ? undefined\n : state.driveItems.find(candidate => candidate.id === item.parentId)\n\n return parent === undefined ? [] : [...pathNames(state, parent), item.name]\n}\n\n/** A stable synthetic GUID for an item's `eTag`/`cTag` (FNV-1a over the id). */\nconst itemGuid = (id: string): string => {\n let hash = 0x811c9dc5\n\n for (const character of id) {\n hash = Math.imul(hash ^ (character.codePointAt(0) ?? 0), 0x01000193) >>> 0\n }\n\n return `${hash.toString(16).padStart(8, '0')}-0000-4000-8000-000000000000`\n}\n\n/** The full item, in the fixture key order. */\nconst renderItem = (\n state: MicrosoftEmulatorState,\n env: MicrosoftApiEnv,\n item: MicrosoftEmulatorDriveItem\n): Schema.JsonObject => {\n const site = personalSite(state.user)\n const names = pathNames(state, item)\n const parentPath = names.slice(0, -1)\n\n const parentReference: Schema.JsonObject =\n item.parentId === null\n ? { driveType: state.drive.driveType, driveId: state.drive.id }\n : {\n driveType: state.drive.driveType,\n driveId: state.drive.id,\n id: item.parentId,\n path: `/drive/root:${parentPath.map(name => `/${name}`).join('')}`\n }\n\n const base = {\n id: item.id,\n name: item.name,\n size: sizeOf(state, item),\n webUrl: `${env.sharePointOrigin}/personal/${site}/Documents${names.map(name => `/${encodeURIComponent(name)}`).join('')}`,\n createdDateTime: item.createdDateTime,\n lastModifiedDateTime: item.lastModifiedDateTime,\n eTag: `\"{${itemGuid(item.id)}},1\"`,\n cTag: `\"c:{${itemGuid(item.id)}},0\"`,\n parentReference\n }\n\n if (item.kind === 'folder') {\n return { ...base, folder: { childCount: childrenOf(state, item.id).length } }\n }\n\n return {\n ...base,\n file: {\n mimeType: item.mimeType ?? 'application/octet-stream',\n hashes: item.quickXorHash === null ? {} : { quickXorHash: item.quickXorHash }\n }\n }\n}\n\n/** `drives('{driveId}')`, the context prefix of every OneDrive route. */\nconst driveContext = (request: RouteRequest): string =>\n `drives${odataKey(request.params.driveId ?? '')}`\n\n/** `drives('{driveId}')/items('{itemId}')/children`. */\nconst childrenContext = (request: RouteRequest): string =>\n `${driveContext(request)}/items${odataKey(request.params.itemId ?? '')}/children`\n\n/** The drive of the `{driveId}` segment, or 404. */\nconst driveProblem = (state: MicrosoftEmulatorState, request: RouteRequest) =>\n request.params.driveId === state.drive.id ? undefined : notFound(request)\n\n/** The item of an id segment (`root` names the drive root). */\nconst findItem = (\n state: MicrosoftEmulatorState,\n id: string\n): MicrosoftEmulatorDriveItem | undefined => {\n const resolved = id.toLowerCase() === 'root' ? state.drive.rootId : id\n\n return state.driveItems.find(item => item.id === resolved)\n}\n\n/** `GET /drives/{driveId}/items/{itemId}` (`$select`). */\nexport const getItem: RouteHandler = (state, request, env) => {\n const drive = driveProblem(state, request)\n\n if (drive !== undefined) return drive\n\n const fields = selectedFields(request, itemFields)\n\n if (fields instanceof Response) return fields\n\n const item = findItem(state, request.params.itemId ?? '')\n\n const context = metadataContext(\n env,\n `${driveContext(request)}/items${selectSuffix(fields)}/$entity`\n )\n\n return item === undefined\n ? notFound(request)\n : jsonResponse(200, entity(context, project(renderItem(state, env, item), fields)))\n}\n\nconst byName = (left: MicrosoftEmulatorDriveItem, right: MicrosoftEmulatorDriveItem) =>\n left.name.localeCompare(right.name, 'en', { sensitivity: 'base' }) ||\n left.id.localeCompare(right.id)\n\n/** The largest children `$top` a fixture sends (`$top=200`, create-folder and copy cases). */\nconst childrenMaxTop = 200\n\n/**\n * `GET /drives/{driveId}/items/{itemId}/children`: by name, at most `$top` (one page only;\n * `$top` required, at most `childrenMaxTop`).\n */\nexport const listChildren: RouteHandler = (state, request, env) => {\n const drive = driveProblem(state, request)\n\n if (drive !== undefined) return drive\n\n const fields = selectedFields(request, itemFields)\n\n if (fields instanceof Response) return fields\n\n const parent = findItem(state, request.params.itemId ?? '')\n\n if (parent === undefined) return notFound(request)\n\n if (parent.kind !== 'folder') return notEmulated(request, 'children of a file are not emulated.')\n\n const page = singlePage([...childrenOf(state, parent.id)].sort(byName), request, childrenMaxTop)\n\n if (page instanceof Response) return page\n\n return jsonResponse(\n 200,\n collection(\n metadataContext(env, `${childrenContext(request)}${selectSuffix(fields)}`),\n page.map(item => project(renderItem(state, env, item), fields)),\n undefined\n )\n )\n}\n\nconst invalidNamePattern = /[\"*:<>?/\\\\|]/\n\nconst nameProblem = (request: RouteRequest, name: unknown): string | Response =>\n Predicate.isString(name) &&\n name.trim() !== '' &&\n name.trim() === name &&\n !invalidNamePattern.test(name) &&\n name !== '.' &&\n name !== '..' &&\n !name.endsWith('.')\n ? name\n : invalidValue(request, 'the item name is empty or has characters OneDrive does not allow.')\n\nconst nameTaken = (state: MicrosoftEmulatorState, parentId: string, name: string): boolean =>\n childrenOf(state, parentId).some(child => child.name.toLowerCase() === name.toLowerCase())\n\nconst nextItemId = (state: MicrosoftEmulatorState): string => {\n let number = state.counters.nextItemNumber\n let id = `01SYNTHETICITEM${padded(number, 17)}`\n\n while (state.driveItems.some(item => item.id === id)) {\n number += 1\n id = `01SYNTHETICITEM${padded(number, 17)}`\n }\n\n state.counters = { ...state.counters, nextItemNumber: number + 1 }\n\n return id\n}\n\nconst nameConflict = (request: RouteRequest): Response =>\n notEmulated(request, 'name conflicts are not emulated (the name is already taken).')\n\n/** Only `fail`, as the fixtures send it; anything else (or none) is not emulated. */\nconst conflictBehaviorProblem = (request: RouteRequest, value: unknown): Response | undefined =>\n value === 'fail'\n ? undefined\n : notEmulated(\n request,\n '@microsoft.graph.conflictBehavior must be fail (other or missing values are not emulated).'\n )\n\n/**\n * `POST /drives/{driveId}/items/{itemId}/children` with a `folder` facet and\n * `@microsoft.graph.conflictBehavior: fail`: 201 with the folder. A taken name, and creating\n * files here, are not emulated.\n */\nexport const createFolder: RouteHandler = (state, request, env) => {\n const drive = driveProblem(state, request)\n\n if (drive !== undefined) return drive\n\n const parent = findItem(state, request.params.itemId ?? '')\n\n if (parent === undefined) return notFound(request)\n\n if (parent.kind !== 'folder') return invalidValue(request, 'the parent item is not a folder.')\n\n const fields = bodyObject(request, ['name', 'folder', '@microsoft.graph.conflictBehavior'])\n\n if (fields instanceof Response) return fields\n\n if (fields.folder === undefined) {\n return notEmulated(request, 'only folder creation (a folder facet) is emulated here.')\n }\n\n const folderFacet = fields.folder\n\n if (!isJsonObject(folderFacet) || Object.keys(folderFacet).length > 0) {\n return notEmulated(request, 'the folder facet must be {}.')\n }\n\n const name = nameProblem(request, fields.name)\n\n if (name instanceof Response) return name\n\n const behavior = conflictBehaviorProblem(request, fields['@microsoft.graph.conflictBehavior'])\n\n if (behavior !== undefined) return behavior\n\n if (nameTaken(state, parent.id, name)) return nameConflict(request)\n\n const now = nowTimestamp(env)\n\n const folder: MicrosoftEmulatorDriveItem = {\n id: nextItemId(state),\n parentId: parent.id,\n name,\n kind: 'folder',\n size: 0,\n mimeType: null,\n quickXorHash: null,\n createdDateTime: now,\n lastModifiedDateTime: now\n }\n\n state.driveItems = [...state.driveItems, folder]\n\n return jsonResponse(\n 201,\n entity(\n metadataContext(env, `${childrenContext(request)}/$entity`),\n renderItem(state, env, folder)\n )\n )\n}\n\n/** `DELETE /drives/{driveId}/items/{itemId}`: 204; the item and its subtree are removed. */\nexport const deleteItem: RouteHandler = (state, request) => {\n const drive = driveProblem(state, request)\n\n if (drive !== undefined) return drive\n\n const item = findItem(state, request.params.itemId ?? '')\n\n if (item === undefined) return notFound(request)\n\n if (item.parentId === null)\n return notEmulated(request, 'deleting the drive root is not emulated.')\n\n const removed = new Set(subtreeOf(state, item).map(entry => entry.id))\n\n state.driveItems = state.driveItems.filter(entry => !removed.has(entry.id))\n\n return emptyResponse(204)\n}\n\nconst monitorPath = (state: MicrosoftEmulatorState, id: string) =>\n `/personal/${personalSite(state.user)}/_api/v2.0/monitor/${id}`\n\n/**\n * `POST /drives/{driveId}/items/{itemId}/copy` (`{ parentReference: { driveId, id } }`, query\n * `@microsoft.graph.conflictBehavior=fail`): 202 with an empty body and one monitor `Location`.\n * The copy itself runs when the monitor reports completion. Only what the copy fixture sends is\n * emulated: a file source, a destination on the same drive named by `driveId` and `id`, and the\n * source's name (a new `name`, a folder source, or a missing `driveId` fail closed).\n */\nexport const copyItem: RouteHandler = (state, request, env) => {\n const drive = driveProblem(state, request)\n\n if (drive !== undefined) return drive\n\n const source = findItem(state, request.params.itemId ?? '')\n\n if (source === undefined) return notFound(request)\n\n if (source.kind !== 'file') {\n return notEmulated(request, 'copying a folder is not emulated (the fixture copies a file).')\n }\n\n const behavior = conflictBehaviorProblem(\n request,\n request.query.get('@microsoft.graph.conflictBehavior') ?? undefined\n )\n\n if (behavior !== undefined) return behavior\n\n // A new `name` fails here too: the fixture's copy keeps the source's name.\n const fields = bodyObject(request, ['parentReference'])\n\n if (fields instanceof Response) return fields\n\n const reference = nestedObject(\n request,\n fields.parentReference,\n ['driveId', 'id'],\n 'parentReference { driveId, id }'\n )\n\n if (reference instanceof Response) return reference\n\n if (reference.driveId === undefined) {\n return notEmulated(request, 'copies without parentReference.driveId are not emulated.')\n }\n\n if (!Predicate.isString(reference.id) || !Predicate.isString(reference.driveId)) {\n return invalidValue(request, 'parentReference must be { driveId, id } with string values.')\n }\n\n if (reference.driveId !== state.drive.id) {\n return notEmulated(request, 'copies to another drive are not emulated.')\n }\n\n const destination = findItem(state, reference.id)\n\n if (destination === undefined) return notFound(request)\n\n if (destination.kind !== 'folder') {\n return invalidValue(request, 'the destination is not a folder.')\n }\n\n const number = env.monitorCounter.next\n\n env.monitorCounter.next += 1\n\n const monitor: CopyMonitor = {\n id: `00000000-0000-4000-8000-${padded(number, 12)}`,\n sourceId: source.id,\n destinationParentId: destination.id,\n pollsLeft: env.copyInProgressPolls,\n resourceId: undefined\n }\n\n env.monitors.set(monitor.id, monitor)\n\n return emptyResponse(202, {\n location: `${env.sharePointOrigin}${monitorPath(state, monitor.id)}`\n })\n}\n\n/** Copy the file `item` under `parentId` with a fresh id and its name; returns the new id. */\nconst copyFile = (\n state: MicrosoftEmulatorState,\n env: MicrosoftApiEnv,\n item: MicrosoftEmulatorDriveItem,\n parentId: string\n): string => {\n const now = nowTimestamp(env)\n\n const copy: MicrosoftEmulatorDriveItem = {\n ...item,\n id: nextItemId(state),\n parentId,\n createdDateTime: now,\n lastModifiedDateTime: now\n }\n\n state.driveItems = [...state.driveItems, copy]\n\n return copy.id\n}\n\n/**\n * Run a monitored copy: its `resourceId`, or why it cannot run (the source or destination is\n * gone, or the name is taken; no fixture records a failed copy).\n */\nconst runCopy = (\n state: MicrosoftEmulatorState,\n env: MicrosoftApiEnv,\n monitor: CopyMonitor\n): string | { readonly problem: string } => {\n const source = findItem(state, monitor.sourceId)\n const destination = findItem(state, monitor.destinationParentId)\n\n if (source === undefined || destination === undefined || destination.kind !== 'folder') {\n return { problem: 'the copy source or destination no longer exists' }\n }\n\n if (nameTaken(state, destination.id, source.name)) {\n return { problem: 'the copy name is already taken in the destination' }\n }\n\n return copyFile(state, env, source, destination.id)\n}\n\nconst monitorContentType = 'application/json;odata.metadata=minimal;odata.streaming=true'\n\n/**\n * `GET /personal/{site}/_api/v2.0/monitor/{monitorId}` on the SharePoint origin (no credentials\n * needed, like the real capability URL): `inProgress` (202) while polls remain, then the copy runs\n * once and every later poll answers `completed` (200, `resourceId`). A copy that cannot run\n * answers 400 not emulated (failed copies are not emulated) and stays pending.\n */\nexport const copyMonitor: RouteHandler = (state, request, env) => {\n const monitor = env.monitors.get(request.params.monitorId ?? '')\n\n if (monitor === undefined || request.params.site !== personalSite(state.user)) {\n return notFound(request)\n }\n\n const context = `${env.sharePointOrigin}/personal/${personalSite(state.user)}/_api/v2.0/$metadata#oneDrive.asynchronousOperationStatus`\n const headers = { 'content-type': monitorContentType }\n\n if (monitor.resourceId === undefined && monitor.pollsLeft > 0) {\n monitor.pollsLeft -= 1\n\n return jsonResponse(\n 202,\n { '@odata.context': context, percentageComplete: 0, status: 'inProgress' },\n headers\n )\n }\n\n const resourceId = monitor.resourceId ?? runCopy(state, env, monitor)\n\n if (!Predicate.isString(resourceId)) {\n return notEmulated(request, `${resourceId.problem} (failed copies are not emulated).`)\n }\n\n monitor.resourceId = resourceId\n\n return jsonResponse(\n 200,\n {\n '@odata.context': context,\n percentageComplete: 100,\n resourceId,\n status: 'completed'\n },\n headers\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { MicrosoftEmulatorState, MicrosoftEmulatorUser } from "./state.mjs";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
|
|
4
|
+
//#region src/microsoft/graph.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Error codes the emulator answers with. `ErrorItemNotFound`, `itemNotFound`, and
|
|
7
|
+
* `ErrorIrresolvableConflict` are copied from the fixtures; `InvalidAuthenticationToken` (missing
|
|
8
|
+
* bearer), `ErrorInvalidUser` (a user other than the seeded mailbox), and `TooManyRequests` (the
|
|
9
|
+
* default 429 fault body) are documented Graph codes no fixture records; the `Synthetic*` codes
|
|
10
|
+
* are emulator codes for requests it refuses to emulate (fail closed) or cannot answer.
|
|
11
|
+
*/
|
|
12
|
+
declare const microsoftEmulatorErrorCodes: {
|
|
13
|
+
readonly mailItemNotFound: "ErrorItemNotFound";
|
|
14
|
+
readonly driveItemNotFound: "itemNotFound";
|
|
15
|
+
readonly conflict: "ErrorIrresolvableConflict";
|
|
16
|
+
readonly unauthenticated: "InvalidAuthenticationToken";
|
|
17
|
+
readonly invalidUser: "ErrorInvalidUser";
|
|
18
|
+
readonly rateLimited: "TooManyRequests";
|
|
19
|
+
readonly unknownRoute: "SyntheticRouteNotEmulated";
|
|
20
|
+
readonly unsupportedQuery: "SyntheticQueryNotEmulated";
|
|
21
|
+
readonly unsupportedValue: "SyntheticValueNotEmulated";
|
|
22
|
+
readonly invalidRequest: "SyntheticInvalidRequest";
|
|
23
|
+
readonly invalidBody: "SyntheticInvalidBody";
|
|
24
|
+
readonly upstreamError: "SyntheticUpstreamError";
|
|
25
|
+
};
|
|
26
|
+
declare const codes: {
|
|
27
|
+
readonly mailItemNotFound: "ErrorItemNotFound";
|
|
28
|
+
readonly driveItemNotFound: "itemNotFound";
|
|
29
|
+
readonly conflict: "ErrorIrresolvableConflict";
|
|
30
|
+
readonly unauthenticated: "InvalidAuthenticationToken";
|
|
31
|
+
readonly invalidUser: "ErrorInvalidUser";
|
|
32
|
+
readonly rateLimited: "TooManyRequests";
|
|
33
|
+
readonly unknownRoute: "SyntheticRouteNotEmulated";
|
|
34
|
+
readonly unsupportedQuery: "SyntheticQueryNotEmulated";
|
|
35
|
+
readonly unsupportedValue: "SyntheticValueNotEmulated";
|
|
36
|
+
readonly invalidRequest: "SyntheticInvalidRequest";
|
|
37
|
+
readonly invalidBody: "SyntheticInvalidBody";
|
|
38
|
+
readonly upstreamError: "SyntheticUpstreamError";
|
|
39
|
+
};
|
|
40
|
+
/** Graph JSON content type, as the fixtures record it. */
|
|
41
|
+
declare const graphContentType = "application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false; charset=utf-8";
|
|
42
|
+
declare const jsonResponse: (status: number, body: unknown, headers?: HeadersInit) => Response;
|
|
43
|
+
/** Graph API version prefix of every Graph route. */
|
|
44
|
+
declare const microsoftEmulatorBasePath = "/v1.0";
|
|
45
|
+
/** A percent-decoded path segment, or `undefined` for invalid percent-encoding. */
|
|
46
|
+
declare const decodeSegment: (segment: string) => string | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* An OData key segment as the fixtures write it in `@odata.context`: `('ada%40example.test')`,
|
|
49
|
+
* `('b%21synthetic-drive-0001')` (percent-encoded, including `!'()*`).
|
|
50
|
+
*/
|
|
51
|
+
declare const odataKey: (value: string) => string;
|
|
52
|
+
/** `(a,b,c)` for a `$select`, empty without one, as the fixture contexts write it. */
|
|
53
|
+
declare const selectSuffix: (fields: ReadonlyArray<string> | undefined) => string;
|
|
54
|
+
/** The fixture `@odata.context` URL: `{graphOrigin}/v1.0/$metadata#{path}`. */
|
|
55
|
+
declare const metadataContext: (env: MicrosoftApiEnv, path: string) => string;
|
|
56
|
+
/** An entity body with its `@odata.context` first, as the fixtures record it. */
|
|
57
|
+
declare const entity: (context: string, body: Schema.JsonObject) => Schema.JsonObject;
|
|
58
|
+
/** A bodiless success (204 delete, 202 cancel/copy), as the fixtures record them. */
|
|
59
|
+
declare const emptyResponse: (status: number, headers?: HeadersInit) => Response;
|
|
60
|
+
/** Where a Graph error envelope's `innerError` values come from. */
|
|
61
|
+
type ErrorContext = {
|
|
62
|
+
/** `innerError.date` (`YYYY-MM-DDTHH:MM:SS`, UTC). */readonly date: string;
|
|
63
|
+
readonly requestId: string;
|
|
64
|
+
readonly clientRequestId: string;
|
|
65
|
+
};
|
|
66
|
+
/** The Graph error envelope `{ error: { code, message, innerError } }`. */
|
|
67
|
+
declare const graphError: (context: ErrorContext, status: number, code: string, message: string) => Response;
|
|
68
|
+
declare const padded: (value: number, width: number) => string;
|
|
69
|
+
/** A synthetic request id for ledger sequence `seq`. */
|
|
70
|
+
declare const syntheticRequestId: (seq: number) => string;
|
|
71
|
+
/** The error context of one request; `client-request-id` is echoed only when it is plain. */
|
|
72
|
+
declare const errorContext: (now: number, seq: number, clientRequestId: string | null) => ErrorContext;
|
|
73
|
+
/** The parsed `Prefer` preferences the emulator reads (others are ignored, as Graph does). */
|
|
74
|
+
type Preferences = {
|
|
75
|
+
/** `IdType="ImmutableId"`: answer message ids in immutable form. */readonly immutableId: boolean; /** `outlook.timezone`, when sent. */
|
|
76
|
+
readonly timezone: string | undefined;
|
|
77
|
+
};
|
|
78
|
+
/** Parse `Prefer`: comma-separated `name=value` preferences. */
|
|
79
|
+
declare const parsePreferences: (header: string | null) => Preferences;
|
|
80
|
+
/** One routed API request, as the handlers see it. */
|
|
81
|
+
type RouteRequest = {
|
|
82
|
+
/** Decoded path parameters. */readonly params: Readonly<Record<string, string>>; /** Raw (still percent-encoded) path, for example `/v1.0/users/ada%40example.test/...`. */
|
|
83
|
+
readonly path: string;
|
|
84
|
+
readonly query: URLSearchParams; /** Parsed JSON body; `undefined` when absent. */
|
|
85
|
+
readonly body: Schema.Json | undefined;
|
|
86
|
+
readonly prefer: Preferences; /** A Graph error envelope response for this request. */
|
|
87
|
+
readonly error: (status: number, code: string, message: string) => Response;
|
|
88
|
+
};
|
|
89
|
+
/** Drill knobs (tests only): each makes the emulator disagree with one conformance claim. */
|
|
90
|
+
type MicrosoftEmulatorDrills = {
|
|
91
|
+
/** `true`: `calendarView` answers an empty `value` for every range. */readonly calendarRangeEmpty?: boolean; /** `true`: event create answers 201 without the new event's `id` (the event is still created). */
|
|
92
|
+
readonly createOmitsId?: boolean; /** Fractional digits of event `dateTime` values (Graph uses 7); `3` drills the precision case. */
|
|
93
|
+
readonly timestampPrecisionDigits?: number; /** `true`: folder message pages never carry `@odata.nextLink`. */
|
|
94
|
+
readonly omitNextLink?: boolean;
|
|
95
|
+
};
|
|
96
|
+
/** A OneDrive copy the monitor reports on (runtime-only, like the ledger; not in the state). */
|
|
97
|
+
type CopyMonitor = {
|
|
98
|
+
readonly id: string;
|
|
99
|
+
readonly sourceId: string;
|
|
100
|
+
readonly destinationParentId: string; /** In-progress answers left before the copy runs and completes. */
|
|
101
|
+
pollsLeft: number; /** Set once the copy ran and completed. */
|
|
102
|
+
resourceId: string | undefined;
|
|
103
|
+
};
|
|
104
|
+
type MicrosoftApiEnv = {
|
|
105
|
+
/** Clock in epoch milliseconds (timestamps of created items). */readonly now: () => number; /** Origin of `@odata.nextLink` values, for example `https://graph.microsoft.com`. */
|
|
106
|
+
readonly graphOrigin: string; /** Origin of OneDrive `webUrl` values and copy monitor URLs. */
|
|
107
|
+
readonly sharePointOrigin: string;
|
|
108
|
+
readonly drills: Required<MicrosoftEmulatorDrills>; /** In-progress monitor answers before a copy completes (default 0: the first poll completes). */
|
|
109
|
+
readonly copyInProgressPolls: number; /** How long a message write holds its message; a concurrent write in that time loses (409). */
|
|
110
|
+
readonly conflictWindowMs: number; /** Immutable ids of messages with a write in flight (runtime-only). */
|
|
111
|
+
readonly messageLocks: Set<string>; /** Copy monitors by id (runtime-only; cleared by reset and seed). */
|
|
112
|
+
readonly monitors: Map<string, CopyMonitor>;
|
|
113
|
+
readonly monitorCounter: {
|
|
114
|
+
next: number;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* A message write's answer and the message it holds once committed: the write holds it for
|
|
119
|
+
* `conflictWindowMs` before answering, so an overlapping write to it gets 409.
|
|
120
|
+
*/
|
|
121
|
+
type HeldAnswer = {
|
|
122
|
+
readonly response: Response; /** Immutable id of the message held. */
|
|
123
|
+
readonly holds: string;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* A route handler. It runs on a draft of the state and of the copy monitors (see
|
|
127
|
+
* `registerMicrosoftApi`) and decides the whole answer synchronously; it replaces whole state lists
|
|
128
|
+
* and counters, never editing them in place. Any answer that is not 2xx is a refusal.
|
|
129
|
+
*/
|
|
130
|
+
type RouteHandler = (state: MicrosoftEmulatorState, request: RouteRequest, env: MicrosoftApiEnv) => Response | HeldAnswer;
|
|
131
|
+
declare const isJsonObject: (value: Schema.Json | undefined) => value is Schema.JsonObject;
|
|
132
|
+
/** Parsed JSON, or `undefined` for invalid JSON. */
|
|
133
|
+
declare const parseJsonText: (text: string) => Schema.Json | undefined;
|
|
134
|
+
/** The user a `/users/{userId}` segment names (id, `mail`, or UPN, case-insensitive). */
|
|
135
|
+
declare const resolveUser: (state: MicrosoftEmulatorState, request: RouteRequest) => MicrosoftEmulatorUser | Response;
|
|
136
|
+
/** The `$select` fields, `undefined` without `$select`, or a 400 for a field not emulated. */
|
|
137
|
+
declare const selectedFields: (request: RouteRequest, allowed: ReadonlyArray<string>) => ReadonlyArray<string> | undefined | Response;
|
|
138
|
+
/** Keep only the selected keys (plus annotations and `id`); everything without `$select`. */
|
|
139
|
+
declare const project: (full: Schema.JsonObject, fields: ReadonlyArray<string> | undefined) => Schema.JsonObject;
|
|
140
|
+
type Page<A> = {
|
|
141
|
+
readonly items: ReadonlyArray<A>;
|
|
142
|
+
readonly top: number; /** `$skip` of the next page, when there is one. */
|
|
143
|
+
readonly nextSkip: number | undefined;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* One `$top`/`$skip` page of `items`. `$top` is required and at most `maxTop` (see
|
|
147
|
+
* `requiredTop`); no fixture records a default page size.
|
|
148
|
+
*/
|
|
149
|
+
declare const pageOf: <A>(items: ReadonlyArray<A>, request: RouteRequest, maxTop: number) => Page<A> | Response;
|
|
150
|
+
/**
|
|
151
|
+
* A collection whose fixtures send `$top` and never page (calendar views, children listings):
|
|
152
|
+
* the items, or 400 not emulated without `$top`, with `$top` above `maxTop` (see `requiredTop`),
|
|
153
|
+
* or with more items than `$top` (no fixture records a default page size or a second page of
|
|
154
|
+
* these collections).
|
|
155
|
+
*/
|
|
156
|
+
declare const singlePage: <A>(items: ReadonlyArray<A>, request: RouteRequest, maxTop: number) => ReadonlyArray<A> | Response;
|
|
157
|
+
/**
|
|
158
|
+
* The `@odata.nextLink` of a page: the configured Graph origin, the request's raw path (so
|
|
159
|
+
* `/users/ada%40example.test` keeps its `%40`), the carried query keys, then `$top` and `$skip`,
|
|
160
|
+
* encoded like the fixtures (`%24select=id%2Csubject...`). `undefined` on the last page or when
|
|
161
|
+
* the `omitNextLink` drill knob is set. Clients follow it unchanged (opaque).
|
|
162
|
+
*/
|
|
163
|
+
declare const nextLinkOf: (env: MicrosoftApiEnv, request: RouteRequest, page: Page<unknown>, carried: ReadonlyArray<string>) => string | undefined;
|
|
164
|
+
/** `{ @odata.context, value, @odata.nextLink? }`, as the fixture pages record them. */
|
|
165
|
+
declare const collection: (context: string, value: ReadonlyArray<unknown>, nextLink: string | undefined) => {
|
|
166
|
+
'@odata.context': string;
|
|
167
|
+
value: readonly unknown[];
|
|
168
|
+
'@odata.nextLink'?: undefined;
|
|
169
|
+
} | {
|
|
170
|
+
'@odata.context': string;
|
|
171
|
+
value: readonly unknown[];
|
|
172
|
+
'@odata.nextLink': string;
|
|
173
|
+
};
|
|
174
|
+
/** A write body that is a JSON object with only `allowed` keys, or a 400. */
|
|
175
|
+
declare const bodyObject: (request: RouteRequest, allowed: ReadonlyArray<string>) => Schema.JsonObject | Response;
|
|
176
|
+
/** 400 for an invalid value. */
|
|
177
|
+
declare const invalidValue: (request: RouteRequest, message: string) => Response;
|
|
178
|
+
/** 400 for a valid Graph request the emulator does not emulate (fail closed). */
|
|
179
|
+
declare const notEmulated: (request: RouteRequest, message: string) => Response;
|
|
180
|
+
/**
|
|
181
|
+
* A nested JSON object with only `allowed` keys (fail closed on anything else, before any write),
|
|
182
|
+
* or a 400. `expected` names the expected object in the error message.
|
|
183
|
+
*/
|
|
184
|
+
declare const nestedObject: (request: RouteRequest, value: Schema.Json | undefined, allowed: ReadonlyArray<string>, expected: string) => Schema.JsonObject | Response;
|
|
185
|
+
/**
|
|
186
|
+
* An `itemBody` write value, `{ contentType, content }` with only those keys. The fixtures write
|
|
187
|
+
* text bodies only (`Text`/`text`); any other content type is not emulated.
|
|
188
|
+
*/
|
|
189
|
+
declare const textBody: (request: RouteRequest, value: Schema.Json | undefined) => string | Response;
|
|
190
|
+
/** A Graph timestamp (`2026-09-29T10:00:00Z`) from the emulator clock. */
|
|
191
|
+
declare const nowTimestamp: (env: MicrosoftApiEnv) => string;
|
|
192
|
+
/** The next change key (bumps the counter). */
|
|
193
|
+
declare const nextChangeKey: (state: MicrosoftEmulatorState, prefix: string) => string;
|
|
194
|
+
/** The `personal/{site}` segment of the mailbox owner (`ada@example.test` → `ada_example_test`). */
|
|
195
|
+
declare const personalSite: (user: MicrosoftEmulatorUser) => string;
|
|
196
|
+
//#endregion
|
|
197
|
+
export { CopyMonitor, ErrorContext, HeldAnswer, MicrosoftApiEnv, MicrosoftEmulatorDrills, Page, Preferences, RouteHandler, RouteRequest, bodyObject, codes, collection, decodeSegment, emptyResponse, entity, errorContext, graphContentType, graphError, invalidValue, isJsonObject, jsonResponse, metadataContext, microsoftEmulatorBasePath, microsoftEmulatorErrorCodes, nestedObject, nextChangeKey, nextLinkOf, notEmulated, nowTimestamp, odataKey, padded, pageOf, parseJsonText, parsePreferences, personalSite, project, resolveUser, selectSuffix, selectedFields, singlePage, syntheticRequestId, textBody };
|
|
198
|
+
//# sourceMappingURL=graph.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.d.mts","names":[],"sources":["../../src/microsoft/graph.ts"],"mappings":"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"}
|