@microsoft/managed-apps-dev-gateway 0.3.1
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 +187 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/DevelopmentMiddleware.d.ts +20 -0
- package/dist/resources/DevelopmentMiddleware.d.ts.map +1 -0
- package/dist/resources/DevelopmentMiddleware.js +117 -0
- package/dist/resources/DevelopmentMiddleware.js.map +1 -0
- package/dist/resources/PackagedResources.d.ts +10 -0
- package/dist/resources/PackagedResources.d.ts.map +1 -0
- package/dist/resources/PackagedResources.js +14 -0
- package/dist/resources/PackagedResources.js.map +1 -0
- package/dist/resources/appScript.js +69 -0
- package/dist/resources/loader.html +98 -0
- package/dist/resources/localDevWorker.js +68 -0
- package/dist/snapshot/OobRevision.d.ts +38 -0
- package/dist/snapshot/OobRevision.d.ts.map +1 -0
- package/dist/snapshot/OobRevision.js +40 -0
- package/dist/snapshot/OobRevision.js.map +1 -0
- package/dist/snapshot/PathPolicy.d.ts +69 -0
- package/dist/snapshot/PathPolicy.d.ts.map +1 -0
- package/dist/snapshot/PathPolicy.js +129 -0
- package/dist/snapshot/PathPolicy.js.map +1 -0
- package/dist/snapshot/SnapshotReader.d.ts +63 -0
- package/dist/snapshot/SnapshotReader.d.ts.map +1 -0
- package/dist/snapshot/SnapshotReader.js +182 -0
- package/dist/snapshot/SnapshotReader.js.map +1 -0
- package/dist/snapshot/SnapshotSource.d.ts +49 -0
- package/dist/snapshot/SnapshotSource.d.ts.map +1 -0
- package/dist/snapshot/SnapshotSource.js +81 -0
- package/dist/snapshot/SnapshotSource.js.map +1 -0
- package/dist/snapshot/index.d.ts +9 -0
- package/dist/snapshot/index.d.ts.map +1 -0
- package/dist/snapshot/index.js +8 -0
- package/dist/snapshot/index.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Microsoft Corporation.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# @microsoft/managed-apps-dev-gateway
|
|
2
|
+
|
|
3
|
+
Local development bootstrap resources for Microsoft Managed Apps.
|
|
4
|
+
|
|
5
|
+
This package contains the local development _snapshot reader_:
|
|
6
|
+
the filesystem and config layer that turns a project on disk into one coherent,
|
|
7
|
+
immutable `DevelopmentSnapshot` from `@microsoft/managed-apps-common/gateway`.
|
|
8
|
+
|
|
9
|
+
It also packages the shared gateway-client's compiled `loader.html` and
|
|
10
|
+
`appScript.js` and static `localDevWorker.js` under `dist/resources/`, and exports a Vite-compatible Node/Connect
|
|
11
|
+
resource adapter. No worker implementation is bundled.
|
|
12
|
+
|
|
13
|
+
It is a Node library, not a server. It does not listen on a port, watch files,
|
|
14
|
+
mount itself into Vite, or call a backend. Those are separate layers.
|
|
15
|
+
|
|
16
|
+
The trusted player supplies the authenticated DevLaunch gateway origin on
|
|
17
|
+
LoaderPort through `dev/configureWorker`. The loader registers the local static
|
|
18
|
+
stub and waits for its exact activated controller before transferring worker ports.
|
|
19
|
+
Vite mounting/watchers and player orchestration are later steps; no live deployment
|
|
20
|
+
or browser-import acceptance is implied by this package.
|
|
21
|
+
|
|
22
|
+
## Node/Connect adapter
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { createDevelopmentMiddlewareAsync } from '@microsoft/managed-apps-dev-gateway';
|
|
26
|
+
|
|
27
|
+
const middleware = await createDevelopmentMiddlewareAsync({
|
|
28
|
+
projectRoot: '/path/to/app',
|
|
29
|
+
devOrigin: 'http://localhost:3000',
|
|
30
|
+
playerOrigin: 'https://play.local.managedapps.cloud.dev.microsoft',
|
|
31
|
+
onError: (error) => console.error(error),
|
|
32
|
+
});
|
|
33
|
+
// Step 7 mounts this opt-in handler before Vite's SPA fallback.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The factory returns `DevelopmentMiddleware`, an async `(request, response, next)`
|
|
37
|
+
handler using Node HTTP types. `DevelopmentMiddlewareOptions` is exported too.
|
|
38
|
+
It does not change existing `managedApps()` or legacy config serving.
|
|
39
|
+
`playerOrigin` must be an allowlisted Microsoft Apps player origin. Loader requests
|
|
40
|
+
must name that exact origin in `ms_embedderOrigin`; successful loader responses set
|
|
41
|
+
`Content-Security-Policy: frame-ancestors <playerOrigin>`.
|
|
42
|
+
|
|
43
|
+
| Exact path | GET response |
|
|
44
|
+
| ----------------------------- | ----------------------------------------------------------------- |
|
|
45
|
+
| `/_platform/app/loader` | Packaged HTML; bootstrap query allowed |
|
|
46
|
+
| `/_platform/app/appScript.js` | Packaged JavaScript |
|
|
47
|
+
| `/_platform/app/dev-snapshot` | Combined validated snapshot JSON |
|
|
48
|
+
| `/_platform/app/sw.js` | Static importing JavaScript; requires `ms_gatewayOrigin` selector |
|
|
49
|
+
|
|
50
|
+
Other paths call `next()`. Owned paths never fall through on failure. Only GET is
|
|
51
|
+
supported (including no HEAD/OPTIONS shortcut); other methods return 405.
|
|
52
|
+
Queries on snapshot/bridge resources return 400. The worker accepts exactly one
|
|
53
|
+
`ms_gatewayOrigin` and no other query keys. Missing/invalid selector returns 400.
|
|
54
|
+
Paths are matched literally without
|
|
55
|
+
decoding/normalizing aliases. Responses have explicit MIME/length, `no-store`,
|
|
56
|
+
`nosniff`, no redirect and no CORS grants.
|
|
57
|
+
|
|
58
|
+
Every owned request requires exactly one Host header equal to `devOrigin`'s
|
|
59
|
+
`URL.host`, **including port**. Forwarded headers do not establish trust. Snapshot
|
|
60
|
+
requests additionally require `X-Managed-Apps-Dev-Snapshot: 1`; any explicit Origin
|
|
61
|
+
must equal the configured origin, and `Sec-Fetch-Site: cross-site` is rejected.
|
|
62
|
+
Missing Origin/Fetch Metadata is allowed for same-origin GET/client compatibility.
|
|
63
|
+
Explicit `null` Origin is rejected. The header is a CSRF/exposure barrier, **not
|
|
64
|
+
authentication** or proof of DevLaunch. There is no arbitrary file or script-path selector.
|
|
65
|
+
|
|
66
|
+
The worker selector must be an HTTPS origin only, without userinfo, query, fragment
|
|
67
|
+
or non-root path. The static stub parses its own registration URL, sets
|
|
68
|
+
`globalThis.__MAAF_LOCAL_DEV_WORKER__ = true`, then imports only the selected
|
|
69
|
+
origin's `/.ms/app/sw.js`. The response adds `Service-Worker-Allowed: /`. Its bytes do not
|
|
70
|
+
vary with the selector; Node neither fetches the source nor independently proves
|
|
71
|
+
RP provenance. This is the approved browser-owned trust boundary, not isolation
|
|
72
|
+
from hostile same-origin code. No source allowlist/pinning or fallback worker exists.
|
|
73
|
+
|
|
74
|
+
Each snapshot request serializes a refresh through `DevelopmentSnapshotSource`.
|
|
75
|
+
It exposes only `snapshot`, never retained init bytes or filesystem diagnostics.
|
|
76
|
+
Invalid refreshes return generic 500 JSON and report details locally through
|
|
77
|
+
`onError` (console by default), never a stale/empty success.
|
|
78
|
+
|
|
79
|
+
## What it does
|
|
80
|
+
|
|
81
|
+
Given an explicit project root, the reader:
|
|
82
|
+
|
|
83
|
+
1. Locates the base config by its well-known file name (`ms.config.json`, then
|
|
84
|
+
`ms.app.config.json`) — the caller never supplies a file path.
|
|
85
|
+
2. Parses it with the shared `ConfigSchemaV0` validator.
|
|
86
|
+
3. If the config declares `data.schemaPath` (or the legacy `db.schemaPath`),
|
|
87
|
+
reads that JSON schema and inlines it under `oob.body.app.manifest.data`.
|
|
88
|
+
Pack writes the schema as a _separate_ file referenced by `dataSchemaPath`;
|
|
89
|
+
the dev session init body needs it inline instead. File reads are bounded
|
|
90
|
+
before parsing: base configs are capped at 2 MiB and schemas at 1 MiB, with
|
|
91
|
+
the byte length checked again after the read.
|
|
92
|
+
4. Copies an explicit `data.enabled` value, or the legacy `db.enabled` fallback,
|
|
93
|
+
to `oob.body.app.manifest.dataEnabled`. Both `true` and `false` are preserved;
|
|
94
|
+
the field is omitted only when neither alias sets it.
|
|
95
|
+
5. Builds the OOB init body, serializes it with the shared
|
|
96
|
+
`serializeInitDevSessionBody` (which enforces the 1 MiB whole-body limit),
|
|
97
|
+
and computes the effective OOB revision.
|
|
98
|
+
6. Validates the assembled snapshot with `DevelopmentSnapshotSchema` and returns
|
|
99
|
+
it frozen, alongside the exact bytes that would be sent to init.
|
|
100
|
+
|
|
101
|
+
`manifest.json` is never required on disk, and no app build or pack is performed.
|
|
102
|
+
|
|
103
|
+
## Revisions
|
|
104
|
+
|
|
105
|
+
Two revisions travel in a snapshot and mean different things:
|
|
106
|
+
|
|
107
|
+
| Field | Meaning |
|
|
108
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| `revision` | Overall snapshot ordering within one bootstrap lifetime. Supplied by the caller; a monotonic safe integer. |
|
|
110
|
+
| `oob.revision` | Opaque digest of the _effective_ OOB inputs — environment identity plus the init body. |
|
|
111
|
+
|
|
112
|
+
`oob.revision` changes if and only if the effective app identity, environment
|
|
113
|
+
identity, or manifest changes. It is insensitive to JSON key order and to
|
|
114
|
+
formatting, so re-saving a config or schema without semantic change does not
|
|
115
|
+
churn it. Connector-only config edits (connection references, connections) and
|
|
116
|
+
asset-only edits leave it untouched: those fields stay in `hostConfig` and are
|
|
117
|
+
deliberately excluded from the OOB body. Changing only `dataEnabled` changes the
|
|
118
|
+
OOB revision and is classified as an OOB update.
|
|
119
|
+
|
|
120
|
+
## Path policy
|
|
121
|
+
|
|
122
|
+
Both the base config and the referenced schema must resolve inside the project
|
|
123
|
+
root. The root itself is canonicalized first, so selecting a root that is itself
|
|
124
|
+
a symlink works consistently.
|
|
125
|
+
|
|
126
|
+
- The schema path must be non-empty, relative, lexically contained, and a
|
|
127
|
+
regular file — the same rules the CLI's `pack` applies.
|
|
128
|
+
- In addition, the _resolved real path_ of both the config and the schema must
|
|
129
|
+
stay under the canonical root. This is **stricter than pack**, which follows a
|
|
130
|
+
link that points outside the project.
|
|
131
|
+
|
|
132
|
+
Path resolution and reading are separate syscalls, so this containment check
|
|
133
|
+
narrows the exposed surface but does not make the reader immune to an adversary
|
|
134
|
+
who can mutate the filesystem concurrently. It is a local development guard, not
|
|
135
|
+
a sandbox.
|
|
136
|
+
|
|
137
|
+
## Exposure boundary
|
|
138
|
+
|
|
139
|
+
The snapshot carries the validated `ConfigSchemaV0` host config plus the inline
|
|
140
|
+
data schema and declared data-API opt-in. Current MHRG rejects OOB requests with
|
|
141
|
+
403 `ManagedAppDataNotEnabled` unless `manifest.dataEnabled` is `true`; the reader
|
|
142
|
+
therefore mirrors `data.enabled` or legacy `db.enabled` without defaulting it.
|
|
143
|
+
It reads no credentials, tokens, or forward-service secrets, and exposes no
|
|
144
|
+
arbitrary-path read API or raw byte passthrough.
|
|
145
|
+
|
|
146
|
+
It is not equivalent to the legacy config endpoint and should not be described
|
|
147
|
+
that way: the inline schema is data that endpoint never carried, and a config
|
|
148
|
+
may contain user-authored content. The snapshot is also **not** an authorization
|
|
149
|
+
artifact — nothing about holding one grants access to anything.
|
|
150
|
+
|
|
151
|
+
## Usage
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import {
|
|
155
|
+
DevelopmentSnapshotSource,
|
|
156
|
+
readDevelopmentSnapshotAsync,
|
|
157
|
+
} from '@microsoft/managed-apps-dev-gateway/snapshot';
|
|
158
|
+
|
|
159
|
+
// One-shot read.
|
|
160
|
+
const { snapshot, initBody } = await readDevelopmentSnapshotAsync({
|
|
161
|
+
projectRoot: '/path/to/app',
|
|
162
|
+
revision: 1,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Or track successive reads, keeping the last valid snapshot on failure.
|
|
166
|
+
const source = new DevelopmentSnapshotSource('/path/to/app');
|
|
167
|
+
const { read, change } = await source.refreshAsync();
|
|
168
|
+
// change: 'initial' | 'metadata' | 'oob' | 'identity'
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
A failed refresh throws a `SnapshotReadError` and leaves the previously valid
|
|
172
|
+
snapshot installed. Invalid input never degrades into an empty manifest.
|
|
173
|
+
|
|
174
|
+
## Scripts
|
|
175
|
+
|
|
176
|
+
| Script | Description |
|
|
177
|
+
| ------------- | --------------------------------------------------------- |
|
|
178
|
+
| `build` | Compile to `dist/` and copy prebuilt loader/bridge assets |
|
|
179
|
+
| `check-types` | Type-check without emitting |
|
|
180
|
+
| `lint` | ESLint over `src/` |
|
|
181
|
+
| `test` | Bun tests |
|
|
182
|
+
| `clean` | Remove build artifacts |
|
|
183
|
+
|
|
184
|
+
Build through `bunx turbo run build --filter=@microsoft/managed-apps-dev-gateway`
|
|
185
|
+
so the build-only gateway-client dependency runs first. Published consumers need
|
|
186
|
+
only the package outputs and runtime dependencies, never the private
|
|
187
|
+
gateway-client package, repository source, esbuild or a TypeScript compiler.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
export type { DevelopmentMiddleware, DevelopmentMiddlewareOptions, } from './resources/DevelopmentMiddleware.js';
|
|
5
|
+
export { createDevelopmentMiddlewareAsync } from './resources/DevelopmentMiddleware.js';
|
|
6
|
+
export type { DevelopmentSnapshotRead, ReadDevelopmentSnapshotOptions } from './snapshot/index.js';
|
|
7
|
+
export { assertRealPathContainedAsync, canonicalizeRootAsync, canonicalJson, computeOobRevision, DevelopmentSnapshotSource, readDevelopmentSnapshotAsync, resolveSchemaPathAsync, SnapshotReadError, SnapshotReadErrorCode, } from './snapshot/index.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EACV,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AACrF,YAAY,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,yBAAyB,EACzB,4BAA4B,EAC5B,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
export { createDevelopmentMiddlewareAsync } from './resources/DevelopmentMiddleware.js';
|
|
5
|
+
export { assertRealPathContainedAsync, canonicalizeRootAsync, canonicalJson, computeOobRevision, DevelopmentSnapshotSource, readDevelopmentSnapshotAsync, resolveSchemaPathAsync, SnapshotReadError, SnapshotReadErrorCode, } from './snapshot/index.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AAErF,OAAO,EACL,4BAA4B,EAC5B,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,yBAAyB,EACzB,4BAA4B,EAC5B,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
+
export interface DevelopmentMiddlewareOptions {
|
|
6
|
+
projectRoot: string;
|
|
7
|
+
/** Exact direct-local browser origin selected by the integrating Vite plugin. */
|
|
8
|
+
devOrigin: string;
|
|
9
|
+
/** Exact allowlisted player origin permitted to frame the privileged loader. */
|
|
10
|
+
playerOrigin: string;
|
|
11
|
+
/** Local diagnostics only; HTTP errors never expose filesystem/config details. */
|
|
12
|
+
onError?: (error: unknown) => void;
|
|
13
|
+
}
|
|
14
|
+
export type DevelopmentMiddleware = (request: IncomingMessage, response: ServerResponse, next: () => void) => Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* A Connect-compatible handler, not a listener. Mount only for explicit local-dev
|
|
17
|
+
* opt-in and before Vite's fallback. Does not modify legacy config serving.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createDevelopmentMiddlewareAsync(options: DevelopmentMiddlewareOptions): Promise<DevelopmentMiddleware>;
|
|
20
|
+
//# sourceMappingURL=DevelopmentMiddleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevelopmentMiddleware.d.ts","sourceRoot":"","sources":["../../src/resources/DevelopmentMiddleware.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAajE,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,MAAM,IAAI,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB;;;GAGG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,qBAAqB,CAAC,CA2GhC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import { DevelopmentResources, DevOriginSchema, parseDevelopmentWorkerSource, PlatformPaths, } from '@microsoft/managed-apps-common/gateway';
|
|
5
|
+
import { maafAllowedEmbedderOrigins } from '@microsoft/managed-apps-common/services';
|
|
6
|
+
import { DevelopmentSnapshotSource } from '../snapshot/SnapshotSource.js';
|
|
7
|
+
import { readPackagedResourcesAsync } from './PackagedResources.js';
|
|
8
|
+
/**
|
|
9
|
+
* A Connect-compatible handler, not a listener. Mount only for explicit local-dev
|
|
10
|
+
* opt-in and before Vite's fallback. Does not modify legacy config serving.
|
|
11
|
+
*/
|
|
12
|
+
export async function createDevelopmentMiddlewareAsync(options) {
|
|
13
|
+
const origin = new URL(DevOriginSchema.parse(options.devOrigin));
|
|
14
|
+
const playerOrigin = new URL(DevOriginSchema.parse(options.playerOrigin)).origin;
|
|
15
|
+
if (!maafAllowedEmbedderOrigins.includes(playerOrigin)) {
|
|
16
|
+
throw new Error('An allowlisted Microsoft Apps player origin is required');
|
|
17
|
+
}
|
|
18
|
+
if (!options.projectRoot.trim())
|
|
19
|
+
throw new Error('An explicit projectRoot is required');
|
|
20
|
+
const assets = await readPackagedResourcesAsync();
|
|
21
|
+
const snapshots = new DevelopmentSnapshotSource(options.projectRoot);
|
|
22
|
+
const reportError = options.onError ?? ((error) => console.error(error));
|
|
23
|
+
const routes = new Set([
|
|
24
|
+
PlatformPaths.loader,
|
|
25
|
+
PlatformPaths.sw,
|
|
26
|
+
DevelopmentResources.appScriptPath,
|
|
27
|
+
DevelopmentResources.snapshotPath,
|
|
28
|
+
]);
|
|
29
|
+
return async (request, response, next) => {
|
|
30
|
+
const rawUrl = request.url ?? '';
|
|
31
|
+
const path = rawUrl.split('?')[0];
|
|
32
|
+
if (!routes.has(path)) {
|
|
33
|
+
next();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const send = (status, contentType, body) => {
|
|
37
|
+
response.statusCode = status;
|
|
38
|
+
response.removeHeader('Access-Control-Allow-Origin');
|
|
39
|
+
response.removeHeader('Access-Control-Allow-Credentials');
|
|
40
|
+
response.removeHeader('Access-Control-Allow-Headers');
|
|
41
|
+
response.removeHeader('Access-Control-Allow-Methods');
|
|
42
|
+
response.setHeader('Content-Type', contentType);
|
|
43
|
+
response.setHeader('Cache-Control', 'no-store');
|
|
44
|
+
response.setHeader('X-Content-Type-Options', 'nosniff');
|
|
45
|
+
response.setHeader('Content-Length', Buffer.byteLength(body));
|
|
46
|
+
response.end(body);
|
|
47
|
+
};
|
|
48
|
+
const fail = (status, code) => send(status, 'application/json; charset=utf-8', JSON.stringify({ error: code }));
|
|
49
|
+
// Forwarded headers never establish origin trust. Host includes the port.
|
|
50
|
+
const hostCount = request.rawHeaders.filter((_value, index) => index % 2 === 0 && request.rawHeaders[index].toLowerCase() === 'host').length;
|
|
51
|
+
if (request.headers.host !== origin.host || hostCount !== 1) {
|
|
52
|
+
fail(403, 'DevelopmentOriginRejected');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (request.method !== 'GET') {
|
|
56
|
+
response.setHeader('Allow', 'GET');
|
|
57
|
+
fail(405, 'MethodNotAllowed');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (path !== PlatformPaths.loader && path !== PlatformPaths.sw && rawUrl !== path) {
|
|
61
|
+
fail(400, 'UnexpectedResourceQuery');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (path === PlatformPaths.sw) {
|
|
65
|
+
try {
|
|
66
|
+
parseDevelopmentWorkerSource(new URL(rawUrl, origin).href);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
fail(400, 'InvalidDevelopmentWorkerSource');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
response.setHeader('Service-Worker-Allowed', '/');
|
|
73
|
+
send(200, 'application/javascript; charset=utf-8', assets.workerStub);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (path === PlatformPaths.loader) {
|
|
77
|
+
const embedderOrigins = new URL(rawUrl, origin).searchParams.getAll('ms_embedderOrigin');
|
|
78
|
+
let requestedPlayerOrigin;
|
|
79
|
+
try {
|
|
80
|
+
if (embedderOrigins.length === 1) {
|
|
81
|
+
requestedPlayerOrigin = new URL(DevOriginSchema.parse(embedderOrigins[0])).origin;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Rejected below with the same non-diagnostic response as any mismatch.
|
|
86
|
+
}
|
|
87
|
+
if (requestedPlayerOrigin !== playerOrigin) {
|
|
88
|
+
fail(403, 'DevelopmentEmbedderOriginRejected');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
response.setHeader('Content-Security-Policy', `frame-ancestors ${playerOrigin}`);
|
|
92
|
+
send(200, 'text/html; charset=utf-8', assets.loaderHtml);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (path === DevelopmentResources.appScriptPath) {
|
|
96
|
+
send(200, 'application/javascript; charset=utf-8', assets.appScript);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const requestOrigin = request.headers.origin;
|
|
100
|
+
if (request.headers[DevelopmentResources.snapshotHeader.toLowerCase()] !==
|
|
101
|
+
DevelopmentResources.snapshotHeaderValue ||
|
|
102
|
+
(requestOrigin !== undefined && requestOrigin !== origin.origin) ||
|
|
103
|
+
request.headers['sec-fetch-site'] === 'cross-site') {
|
|
104
|
+
fail(403, 'DevelopmentSnapshotAccessRejected');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const { read } = await snapshots.refreshAsync();
|
|
109
|
+
send(200, 'application/json; charset=utf-8', JSON.stringify(read.snapshot));
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
fail(500, 'DevelopmentSnapshotUnavailable');
|
|
113
|
+
reportError(error);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=DevelopmentMiddleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevelopmentMiddleware.js","sourceRoot":"","sources":["../../src/resources/DevelopmentMiddleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/** Only these build-owned assets are shipped; the live worker is never packaged. */
|
|
5
|
+
export declare function readPackagedResourcesAsync(): Promise<{
|
|
6
|
+
loaderHtml: string;
|
|
7
|
+
appScript: string;
|
|
8
|
+
workerStub: string;
|
|
9
|
+
}>;
|
|
10
|
+
//# sourceMappingURL=PackagedResources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PackagedResources.d.ts","sourceRoot":"","sources":["../../src/resources/PackagedResources.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,oFAAoF;AACpF,wBAAsB,0BAA0B,IAAI,OAAO,CAAC;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC,CAOD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
/** Only these build-owned assets are shipped; the live worker is never packaged. */
|
|
6
|
+
export async function readPackagedResourcesAsync() {
|
|
7
|
+
const [loaderHtml, appScript, workerStub] = await Promise.all([
|
|
8
|
+
readFile(new URL('./loader.html', import.meta.url), 'utf8'),
|
|
9
|
+
readFile(new URL('./appScript.js', import.meta.url), 'utf8'),
|
|
10
|
+
readFile(new URL('./localDevWorker.js', import.meta.url), 'utf8'),
|
|
11
|
+
]);
|
|
12
|
+
return { loaderHtml, appScript, workerStub };
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=PackagedResources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PackagedResources.js","sourceRoot":"","sources":["../../src/resources/PackagedResources.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,0BAA0B;IAK9C,MAAM,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC5D,QAAQ,CAAC,IAAI,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAC3D,QAAQ,CAAC,IAAI,GAAG,CAAC,gBAAgB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAC5D,QAAQ,CAAC,IAAI,GAAG,CAAC,qBAAqB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;KAClE,CAAC,CAAC;IACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;AAC/C,CAAC"}
|