@crvouga/mockingbird-service-oura 1.6.0 → 1.8.0
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/CHANGELOG.md +14 -1
- package/README.md +10 -2
- package/SUPPORT.md +5 -3
- package/dist/{chunk-L4OVKRH3.js → chunk-NXZZD2JL.js} +115 -15
- package/dist/chunk-NXZZD2JL.js.map +7 -0
- package/dist/{chunk-6IX4G3SW.js → chunk-ZPLFTMD7.js} +204 -27
- package/dist/chunk-ZPLFTMD7.js.map +7 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +60 -6
- package/dist/index.js +3 -1
- package/dist/server.d.ts +95 -0
- package/dist/server.js +2 -2
- package/openapi.yaml +226 -0
- package/package.json +1 -1
- package/dist/chunk-6IX4G3SW.js.map +0 -7
- package/dist/chunk-L4OVKRH3.js.map +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-oura
|
|
2
2
|
|
|
3
|
-
## 1.
|
|
3
|
+
## 1.8.0 (2026-10-21)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- raise the serve and fleet body limit to the S3 maximum ([86b9e2b](https://github.com/crvouga/mockingbird/commit/86b9e2b80bcaaaef57f52edbac50b27da0b42f33))
|
|
8
|
+
- add authorize endpoints that redirect with code and state, and profile endpoints ([a686188](https://github.com/crvouga/mockingbird/commit/a6861889e02552eb31ea7f8c505db12ffc450072))
|
|
9
|
+
|
|
10
|
+
## 1.7.0 (2026-10-18)
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- resolve the agent-reported issue queue in one batch ([25c52b6](https://github.com/crvouga/mockingbird/commit/25c52b668a53ddb80b968419601a20abf33f956b))
|
|
15
|
+
|
|
16
|
+
## 1.6.0 (2026-10-16)
|
|
4
17
|
|
|
5
18
|
### Features
|
|
6
19
|
|
package/README.md
CHANGED
|
@@ -32,6 +32,12 @@ Run `mockingbird-oura serve --port 12128`; inject this origin into the consumer'
|
|
|
32
32
|
|
|
33
33
|
POST `/oauth/token` accepts URL-encoded authorization code or refresh grants, Basic or body client credentials. Seed `clients`, `codes`, `grants` with constructor options or standard state admin routes. Default local client is `mock_client` / `mock_client_secret`; bearer `mock_oura_token` belongs to `synthetic-user`. Codes bind client, redirect, expiry and optional S256 PKCE challenge. Refresh tokens rotate once, retaining user/scopes; clock controls access expiry.
|
|
34
34
|
|
|
35
|
+
GET `/oauth/authorize` is the authorization endpoint a browser visits to connect a member (live: `cloud.ouraring.com/oauth/authorize`). There is no consent page: the request is validated, then the `consents` collection answers for the user and the browser is redirected (302) to `redirect_uri` with `code` and `state`, or `error`, `error_description` and `state`. The code is stored in `codes` bound to the client, the granted scopes, the `redirect_uri` when the caller sent one (the token exchange must then repeat it) and an optional S256 `code_challenge` (the exchange then needs the matching `code_verifier`); `POST /oauth/token` redeems it once. `scope` omitted grants every scope; `state` is optional and echoed unchanged at any length. `redirect_uri` may be omitted when the client has exactly one registered. An unknown client (401 `invalid_client`), a missing `client_id`, a missing or unregistered `redirect_uri` (400 `invalid_request`, JSON `{status,error,error_description}`) is answered in place and never redirected to. Clients take optional `redirectUris` (exact match; omitted accepts any absolute http(s) URI so the default `mock_client` works as is) and `scopes` (omitted allows the documented Oura scopes: `email personal daily heartrate workout tag session spo2`). Redirected errors: `invalid_request` (no `response_type`, or a `code_challenge_method` other than `S256`), `unsupported_response_type`, `invalid_scope`.
|
|
36
|
+
|
|
37
|
+
Consent controls: rows `{id, decision: "approve"|"deny"|"error", userId?, codeTtlSeconds?, error?, errorDescription?}` where `id` is a client id or `*` (every client without its own row). Seed with the `consents` option or `POST /__admin/state/consents {"id":"*","value":{"id":"*","decision":"deny"}}`. No row means approve for `synthetic-user` with a 600 second code. `deny` redirects `access_denied` and `error` redirects `server_error` (override with `error`/`errorDescription`), both with the unchanged `state` and no code. Issued codes are listed at `GET /__admin/state/codes`.
|
|
38
|
+
|
|
39
|
+
GET `/v2/usercollection/personal_info` returns the token user's personal info (`id`, and as seeded `age`, `weight`, `height`, `biological_sex`, `email`): seed a `records` row with `collection: "personal_info"`, `userId` the token's user and `data` the response object (fake values only). Scope `personal` is required (403 `{detail:"Access forbidden."}` like the collections, 401 with the collections' body for an unknown or expired token, 404 `{detail:"Not Found"}` when the user has no row). `data` is returned as seeded: the `email` scope is not enforced per field. The default grant does not carry `personal`.
|
|
40
|
+
|
|
35
41
|
GET `/v2/usercollection/{workout,sleep,heartrate,daily_activity,daily_spo2,daily_readiness,daily_sleep}` returns `{data,next_token}`. Heart rate uses `start_datetime/end_datetime`; others use `start_date/end_date` against `day`. Fixture windows include both endpoints, omitted bounds are unbounded. These boundary/default choices are deterministic local fixture semantics, not verified live edge-case parity. Never invent an id for heart-rate samples. Fixtures retain every supplied metric, including nulls. Scope checks use `workout`, `daily`, `heartrate`, `spo2`.
|
|
36
42
|
|
|
37
43
|
Seed `records` rows `{key,userId,collection,data}`. Keys identify storage rows, not vendor ids; distinct keys can deliberately carry duplicate provider data. `pageSize` controls deterministic insertion-order pages. Consumers must retain the same collection/window while paginating; the emulator's row-key cursors do not bind or validate the query. No promised vendor cursor format/order. Set `tokenTtlSeconds` for newly issued tokens.
|
|
@@ -44,14 +50,16 @@ Standard `/__admin/health`, `/state`, `/reset`, `/requests`, `/clock`, Timeline
|
|
|
44
50
|
|
|
45
51
|
Contract: [official Oura OpenAPI 1.41](https://cloud.ouraring.com/v2/static/json/openapi-1.41.json) and [OAuth documentation](https://cloud.ouraring.com/docs/authentication). Synthetic invalid credentials were checked live: token endpoint returned HTTP 400 `invalid_client`, collection endpoint HTTP 401 with the documented detail. Authorized collection data and refresh were not checked live.
|
|
46
52
|
|
|
53
|
+
**Best guess, not oracle-verified:** the authorization endpoint and `personal_info` are not observed live, and the vendor OpenAPI JSON was not downloadable (HTTP 404) when this was added. The parameters, the all-scopes default and the single-registered-URI default follow the [Oura authentication page](https://cloud.ouraring.com/docs/authentication). The redirect status (302), the `error`/`error_description`/`state` redirect shape, the statuses and bodies for an unknown client or unregistered `redirect_uri`, the `invalid_scope` allow-list, S256-only PKCE, `access_denied`/`server_error` for consent controls and the 403 for a missing `personal` scope are the emulator's choices, not observed vendor responses. The `personal_info` fields (`id`, `age`, `weight`, `height`, `biological_sex`, `email`) come from the Oura v2 API documentation and third-party client documentation; whether the live response carries `id` is unconfirmed.
|
|
54
|
+
|
|
47
55
|
`bun test` runs acceptance and every-operation self-parity including divergence detection. `bun run parity` needs `OURA_ACCESS_TOKEN`, performs safe read-only envelope checks, and never prints health data or credentials.
|
|
48
56
|
|
|
49
57
|
## Deliberately not modelled
|
|
50
58
|
|
|
51
|
-
No real health data, account signup, consent
|
|
59
|
+
No real health data, account signup, a consent page (the `consents` rows stand in for the user's answer), device pairing, sensor simulation, hardware SDK, webhooks, subscription lifecycle, field projection, statistical computation or full vendor metric validation. Records are synthetic pass-through fixtures; partial metrics can be seeded deliberately. Date boundary/default behavior, cursor internals and quota-body details are local stand-ins, not claims of exact vendor edge-case parity. No Python runtime dependency or claim that the private consumer itself was exercised.
|
|
52
60
|
|
|
53
61
|
## API
|
|
54
62
|
|
|
55
|
-
Root runtime exports: `OuraAPI`, `OURA_NAMESPACE`, `COLLECTION_SCOPES`, `createRuntime`, `OURA_PRESETS`, `document`, `operationIds`, `supportedOperationIds`. Types include `OuraAPIOptions`, `Grant`, `OAuthCode`, `Client`, `DataRecord`, `CollectionName`, `OuraRuntimeOptions`, `OuraRuntime`, `OperationId`, `SupportedOperationId`.
|
|
63
|
+
Root runtime exports: `OuraAPI`, `OURA_NAMESPACE`, `COLLECTION_SCOPES`, `KNOWN_SCOPES`, `createRuntime`, `OURA_PRESETS`, `document`, `operationIds`, `supportedOperationIds`. Types include `OuraAPIOptions`, `Grant`, `OAuthCode`, `Client`, `Consent`, `DataRecord`, `CollectionName`, `OuraRuntimeOptions`, `OuraRuntime`, `OperationId`, `SupportedOperationId`.
|
|
56
64
|
|
|
57
65
|
`/server` exports `createServer`, `serveTarget`, `DEFAULT_PORT` and types `OuraServerOptions`, `OuraServer`.
|
package/SUPPORT.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Generated from `openapi.yaml`; do not edit by hand.
|
|
4
4
|
|
|
5
|
-
- operations in spec: **
|
|
6
|
-
- supported by the emulator: **
|
|
7
|
-
- parity enabled: **
|
|
5
|
+
- operations in spec: **10**
|
|
6
|
+
- supported by the emulator: **10**
|
|
7
|
+
- parity enabled: **10**
|
|
8
8
|
|
|
9
9
|
| operationId | route | emulator | parity | notes |
|
|
10
10
|
| --- | --- | --- | --- | --- |
|
|
@@ -15,4 +15,6 @@ Generated from `openapi.yaml`; do not edit by hand.
|
|
|
15
15
|
| `ListDailySpo2` | `GET /v2/usercollection/daily_spo2` | ✅ supported | ✅ | |
|
|
16
16
|
| `ListDailyReadiness` | `GET /v2/usercollection/daily_readiness` | ✅ supported | ✅ | |
|
|
17
17
|
| `ListDailySleep` | `GET /v2/usercollection/daily_sleep` | ✅ supported | ✅ | |
|
|
18
|
+
| `GetPersonalInfo` | `GET /v2/usercollection/personal_info` | ✅ supported | ✅ | |
|
|
18
19
|
| `OAuthToken` | `POST /oauth/token` | ✅ supported | ⚠️ unsafe (opt-in) | |
|
|
20
|
+
| `OAuthAuthorize` | `GET /oauth/authorize` | ✅ supported | ⚠️ unsafe (opt-in) | |
|
|
@@ -3,17 +3,31 @@ import {
|
|
|
3
3
|
createRuntime,
|
|
4
4
|
normalizeBootControls,
|
|
5
5
|
normalizeControls,
|
|
6
|
+
normalizeIdBase,
|
|
6
7
|
ownedTransport,
|
|
7
8
|
parseDuration,
|
|
8
9
|
parseFaultHeader,
|
|
9
10
|
resolveAdminPrefix,
|
|
10
11
|
restoreClock
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ZPLFTMD7.js";
|
|
12
13
|
|
|
13
14
|
// ../../adapters/node/dist/cli.js
|
|
14
15
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
15
16
|
import { parseArgs } from "node:util";
|
|
16
17
|
|
|
18
|
+
// ../../adapters/node/dist/boot-settings.js
|
|
19
|
+
var checkSettings = (target, settings, label) => {
|
|
20
|
+
if (!target.parseSettings)
|
|
21
|
+
throw new Error(`${label}: ${target.name} takes no boot settings`);
|
|
22
|
+
try {
|
|
23
|
+
return target.parseSettings(settings);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
throw new Error(`${label}: ${error instanceof Error ? error.message : String(error)}`, {
|
|
26
|
+
cause: error
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
17
31
|
// ../../adapters/node/dist/fleet.js
|
|
18
32
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
19
33
|
import { createHash as createHash4, randomUUID as randomUUID4 } from "node:crypto";
|
|
@@ -2663,6 +2677,9 @@ var fleet_config_schema_default = {
|
|
|
2663
2677
|
adminControls: {
|
|
2664
2678
|
$ref: "#/$defs/controls"
|
|
2665
2679
|
},
|
|
2680
|
+
idBase: {
|
|
2681
|
+
$ref: "#/$defs/idBase"
|
|
2682
|
+
},
|
|
2666
2683
|
namespace: {
|
|
2667
2684
|
type: "string",
|
|
2668
2685
|
pattern: "^[A-Za-z0-9_.-]{1,64}$"
|
|
@@ -2752,6 +2769,12 @@ var fleet_config_schema_default = {
|
|
|
2752
2769
|
adminControls: {
|
|
2753
2770
|
$ref: "#/$defs/controls"
|
|
2754
2771
|
},
|
|
2772
|
+
idBase: {
|
|
2773
|
+
$ref: "#/$defs/idBase"
|
|
2774
|
+
},
|
|
2775
|
+
settings: {
|
|
2776
|
+
type: "object"
|
|
2777
|
+
},
|
|
2755
2778
|
fixtures: {
|
|
2756
2779
|
type: "object"
|
|
2757
2780
|
},
|
|
@@ -2798,6 +2821,18 @@ var fleet_config_schema_default = {
|
|
|
2798
2821
|
}
|
|
2799
2822
|
}
|
|
2800
2823
|
},
|
|
2824
|
+
idBase: {
|
|
2825
|
+
anyOf: [
|
|
2826
|
+
{
|
|
2827
|
+
type: "integer",
|
|
2828
|
+
minimum: 0
|
|
2829
|
+
},
|
|
2830
|
+
{
|
|
2831
|
+
type: "string",
|
|
2832
|
+
pattern: "^(boot|[0-9]+)$"
|
|
2833
|
+
}
|
|
2834
|
+
]
|
|
2835
|
+
},
|
|
2801
2836
|
controls: {
|
|
2802
2837
|
type: "object",
|
|
2803
2838
|
additionalProperties: false,
|
|
@@ -2947,6 +2982,8 @@ async function preflightFleet(config, options) {
|
|
|
2947
2982
|
throw new Error("protected archive policy requires a protected repository; configure its protector before startup");
|
|
2948
2983
|
resolveAdminPrefix(config.adminPrefix);
|
|
2949
2984
|
normalizeBootControls(config.adminControls);
|
|
2985
|
+
if (config.idBase !== void 0)
|
|
2986
|
+
normalizeIdBase(config.idBase, "idBase");
|
|
2950
2987
|
const targets = /* @__PURE__ */ new Map();
|
|
2951
2988
|
const ports = /* @__PURE__ */ new Set();
|
|
2952
2989
|
for (const [name, entry] of Object.entries(config.services)) {
|
|
@@ -2978,6 +3015,13 @@ async function preflightFleet(config, options) {
|
|
|
2978
3015
|
throw new Error("invalid protocol namespace selector");
|
|
2979
3016
|
}
|
|
2980
3017
|
}
|
|
3018
|
+
if (entry.idBase !== void 0)
|
|
3019
|
+
normalizeIdBase(entry.idBase, "idBase");
|
|
3020
|
+
if (entry.settings !== void 0) {
|
|
3021
|
+
if (protocol !== "http" || !("create" in target))
|
|
3022
|
+
throw new Error("settings: only HTTP services take boot settings");
|
|
3023
|
+
checkSettings(target, entry.settings, "settings");
|
|
3024
|
+
}
|
|
2981
3025
|
const port = entry.port ?? target.defaultPort;
|
|
2982
3026
|
if (port !== 0) {
|
|
2983
3027
|
const key = `${entry.host ?? "127.0.0.1"}:${port}`;
|
|
@@ -3013,7 +3057,7 @@ async function preflightFleet(config, options) {
|
|
|
3013
3057
|
break;
|
|
3014
3058
|
}
|
|
3015
3059
|
}
|
|
3016
|
-
targets.set(name, { ...target, packageVersion: packageVersion ?? "1.
|
|
3060
|
+
targets.set(name, { ...target, packageVersion: packageVersion ?? "1.8.0" });
|
|
3017
3061
|
} catch (error) {
|
|
3018
3062
|
throw new Error(`service ${name}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
3019
3063
|
}
|
|
@@ -3239,7 +3283,11 @@ var serve = async (api, options = {}) => {
|
|
|
3239
3283
|
// ../../adapters/node/dist/listen.js
|
|
3240
3284
|
var listen = async (api, options = {}) => {
|
|
3241
3285
|
const host = options.host ?? "127.0.0.1";
|
|
3242
|
-
const server = await serve(api, {
|
|
3286
|
+
const server = await serve(api, {
|
|
3287
|
+
port: options.port ?? 0,
|
|
3288
|
+
host,
|
|
3289
|
+
...options.maxBodyBytes !== void 0 ? { maxBodyBytes: options.maxBodyBytes } : {}
|
|
3290
|
+
});
|
|
3243
3291
|
const address = server.address();
|
|
3244
3292
|
const port = typeof address === "object" && address !== null ? address.port : options.port ?? 0;
|
|
3245
3293
|
const shown = host.includes(":") ? `[${host}]` : host;
|
|
@@ -3333,6 +3381,7 @@ var httpChild = async (target, entry, options, admission, generation, privateAcc
|
|
|
3333
3381
|
options.onLog?.(entry2);
|
|
3334
3382
|
},
|
|
3335
3383
|
...entry.adminControls !== void 0 ? { adminControls: entry.adminControls } : {},
|
|
3384
|
+
...entry.settings !== void 0 ? { settings: checkSettings(target, entry.settings, "settings") } : {},
|
|
3336
3385
|
...entry.fixtures !== void 0 ? { fixtures: entry.fixtures } : {},
|
|
3337
3386
|
...(advertisedHttpBase(entry) ?? entry.baseUrl) !== void 0 ? { baseUrl: advertisedHttpBase(entry) ?? entry.baseUrl } : {}
|
|
3338
3387
|
});
|
|
@@ -3351,6 +3400,8 @@ var httpChild = async (target, entry, options, admission, generation, privateAcc
|
|
|
3351
3400
|
].some((method) => typeof created[method] !== "function"))
|
|
3352
3401
|
throw new Error("HTTP target does not implement the fleet runtime protocol; build/install matching supported service versions");
|
|
3353
3402
|
const runtime = created;
|
|
3403
|
+
if (entry.idBase !== void 0)
|
|
3404
|
+
runtime.setIdBase?.(entry.idBase);
|
|
3354
3405
|
const unobserve = runtime.webhooks?.observeDeliveries(observer.delivery);
|
|
3355
3406
|
if (options.outbound && runtime.webhooks)
|
|
3356
3407
|
runtime.webhooks.setTransportPolicy(ownedTransport(options.outbound));
|
|
@@ -3435,7 +3486,8 @@ var httpChild = async (target, entry, options, admission, generation, privateAcc
|
|
|
3435
3486
|
try {
|
|
3436
3487
|
server = await listen(api, {
|
|
3437
3488
|
port: entry.port ?? target.defaultPort,
|
|
3438
|
-
host: entry.host ?? "127.0.0.1"
|
|
3489
|
+
host: entry.host ?? "127.0.0.1",
|
|
3490
|
+
...target.maxBodyBytes !== void 0 ? { maxBodyBytes: target.maxBodyBytes } : {}
|
|
3439
3491
|
});
|
|
3440
3492
|
target.listening?.(server);
|
|
3441
3493
|
} catch (error) {
|
|
@@ -3949,6 +4001,7 @@ async function startFleet(config, options) {
|
|
|
3949
4001
|
...entry,
|
|
3950
4002
|
adminPrefix: resolveAdminPrefix(entry.adminPrefix ?? adminPrefix),
|
|
3951
4003
|
...adminKey !== void 0 ? { adminKey } : {},
|
|
4004
|
+
...(entry.idBase ?? config.idBase) !== void 0 ? { idBase: entry.idBase ?? config.idBase } : {},
|
|
3952
4005
|
// Fleet-wide controls, with the service's own keys replacing them.
|
|
3953
4006
|
...config.adminControls !== void 0 || entry.adminControls !== void 0 ? { adminControls: { ...config.adminControls, ...entry.adminControls } } : {}
|
|
3954
4007
|
}, options, (namespace, request) => management?.admit(namespace, request), (namespace) => management?.generation(namespace), (namespace, request) => management?.privateRequest(namespace, request) ?? false, {
|
|
@@ -4633,6 +4686,16 @@ var COMMON_SERVE_OPTIONS = {
|
|
|
4633
4686
|
value: "<seed>",
|
|
4634
4687
|
description: "Seed for every random choice (env MOCKINGBIRD_SEED)"
|
|
4635
4688
|
},
|
|
4689
|
+
"id-base": {
|
|
4690
|
+
type: "string",
|
|
4691
|
+
value: "<n|boot>",
|
|
4692
|
+
description: "Start generated ids at this base so a restart does not reissue the last boot's ids; boot derives one from the clock (env MOCKINGBIRD_ID_BASE; default 0, the same ids every boot)"
|
|
4693
|
+
},
|
|
4694
|
+
settings: {
|
|
4695
|
+
type: "string",
|
|
4696
|
+
value: "<json|file>",
|
|
4697
|
+
description: "Service settings to boot with, as JSON or a JSON file; every namespace starts from them (env MOCKINGBIRD_SETTINGS; only for services that declare settings)"
|
|
4698
|
+
},
|
|
4636
4699
|
"admin-controls": {
|
|
4637
4700
|
type: "string",
|
|
4638
4701
|
value: "<json|file>",
|
|
@@ -4727,19 +4790,40 @@ var readJsonFile = async (path, label) => {
|
|
|
4727
4790
|
}
|
|
4728
4791
|
};
|
|
4729
4792
|
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4793
|
+
var readJsonOption = async (source, label) => {
|
|
4794
|
+
const trimmed = source.trim();
|
|
4795
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
4796
|
+
try {
|
|
4797
|
+
return JSON.parse(trimmed);
|
|
4798
|
+
} catch {
|
|
4799
|
+
throw new Error(`${label}: not valid JSON`);
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
return readJsonFile(source, label);
|
|
4803
|
+
};
|
|
4804
|
+
var resolveServeIdBase = (values, env = process.env) => {
|
|
4805
|
+
const source = asString(values["id-base"]) ?? env.MOCKINGBIRD_ID_BASE;
|
|
4806
|
+
if (source === void 0 || source.trim() === "")
|
|
4807
|
+
return void 0;
|
|
4808
|
+
normalizeIdBase(source.trim(), "--id-base");
|
|
4809
|
+
return source.trim();
|
|
4810
|
+
};
|
|
4811
|
+
var resolveServeSettings = async (target, values, env = process.env) => {
|
|
4812
|
+
const flag = asString(values.settings);
|
|
4813
|
+
const source = flag ?? env.MOCKINGBIRD_SETTINGS;
|
|
4814
|
+
if (source === void 0 || source.trim() === "")
|
|
4815
|
+
return void 0;
|
|
4816
|
+
const label = flag !== void 0 ? "--settings" : "MOCKINGBIRD_SETTINGS";
|
|
4817
|
+
const parsed = await readJsonOption(source, label);
|
|
4818
|
+
if (!isObject2(parsed))
|
|
4819
|
+
throw new Error(`${label}: expected a JSON object`);
|
|
4820
|
+
return checkSettings(target, parsed, label);
|
|
4821
|
+
};
|
|
4730
4822
|
var resolveServeControls = async (values, env = process.env, base) => {
|
|
4731
4823
|
const source = asString(values["admin-controls"]) ?? env.MOCKINGBIRD_ADMIN_CONTROLS;
|
|
4732
4824
|
let document = isObject2(base) ? { ...base } : {};
|
|
4733
4825
|
if (source !== void 0 && source.trim() !== "") {
|
|
4734
|
-
|
|
4735
|
-
if (source.trim().startsWith("{")) {
|
|
4736
|
-
try {
|
|
4737
|
-
parsed = JSON.parse(source);
|
|
4738
|
-
} catch {
|
|
4739
|
-
throw new Error("--admin-controls: not valid JSON");
|
|
4740
|
-
}
|
|
4741
|
-
} else
|
|
4742
|
-
parsed = await readJsonFile(source, "--admin-controls");
|
|
4826
|
+
const parsed = source.trim().startsWith("{") ? await readJsonOption(source, "--admin-controls") : await readJsonFile(source, "--admin-controls");
|
|
4743
4827
|
if (!isObject2(parsed))
|
|
4744
4828
|
throw new Error("--admin-controls: expected a JSON object");
|
|
4745
4829
|
document = { ...document, ...parsed };
|
|
@@ -4789,9 +4873,16 @@ var start = async (target, values, config) => {
|
|
|
4789
4873
|
adminKey: config.adminKey,
|
|
4790
4874
|
seed: config.seed,
|
|
4791
4875
|
onLog: formatLog(config.log),
|
|
4876
|
+
...config.settings !== void 0 ? { settings: config.settings } : {},
|
|
4792
4877
|
...config.adminControls !== void 0 ? { adminControls: config.adminControls } : {}
|
|
4793
4878
|
});
|
|
4794
|
-
|
|
4879
|
+
if (config.idBase !== void 0)
|
|
4880
|
+
runtime.setIdBase?.(config.idBase);
|
|
4881
|
+
const listening = await listen(runtime, {
|
|
4882
|
+
port: config.port,
|
|
4883
|
+
host: config.host,
|
|
4884
|
+
...target.maxBodyBytes !== void 0 ? { maxBodyBytes: target.maxBodyBytes } : {}
|
|
4885
|
+
});
|
|
4795
4886
|
target.listening?.(listening);
|
|
4796
4887
|
console.log(`${target.name} emulator listening on ${listening.url}`);
|
|
4797
4888
|
console.log(`${target.name} health: GET ${listening.url}${adminPrefix}/health`);
|
|
@@ -4836,6 +4927,11 @@ var serveCommand = (target) => ({
|
|
|
4836
4927
|
const adminControls = await resolveServeControls(values, process.env, config.adminControls);
|
|
4837
4928
|
if (adminControls !== void 0)
|
|
4838
4929
|
config.adminControls = adminControls;
|
|
4930
|
+
if (values.settings !== void 0)
|
|
4931
|
+
throw new Error("--settings serves a single service; give each service its own `settings` in the config");
|
|
4932
|
+
const idBase = config.idBase ?? resolveServeIdBase(values);
|
|
4933
|
+
if (idBase !== void 0)
|
|
4934
|
+
config.idBase = idBase;
|
|
4839
4935
|
const fleet = await startFleet(config, {
|
|
4840
4936
|
load: (name) => loadTarget(name, target),
|
|
4841
4937
|
onLog: formatLog(config.log ?? log),
|
|
@@ -4865,8 +4961,12 @@ var serveCommand = (target) => ({
|
|
|
4865
4961
|
let listening;
|
|
4866
4962
|
try {
|
|
4867
4963
|
const adminControls = await resolveServeControls(values);
|
|
4964
|
+
const idBase = resolveServeIdBase(values);
|
|
4965
|
+
const settings = await resolveServeSettings(target, values);
|
|
4868
4966
|
listening = await start(target, values, {
|
|
4869
4967
|
...adminControls !== void 0 ? { adminControls } : {},
|
|
4968
|
+
...idBase !== void 0 ? { idBase } : {},
|
|
4969
|
+
...settings !== void 0 ? { settings } : {},
|
|
4870
4970
|
adminPrefix: resolveAdminPrefix(asString(values["admin-prefix"]) ?? process.env.MOCKINGBIRD_ADMIN_PREFIX),
|
|
4871
4971
|
port: port === void 0 ? target.defaultPort : Number.parseInt(port, 10),
|
|
4872
4972
|
host: asString(values.host) ?? "127.0.0.1",
|
|
@@ -4911,4 +5011,4 @@ export {
|
|
|
4911
5011
|
createServer2 as createServer,
|
|
4912
5012
|
serveTarget
|
|
4913
5013
|
};
|
|
4914
|
-
//# sourceMappingURL=chunk-
|
|
5014
|
+
//# sourceMappingURL=chunk-NXZZD2JL.js.map
|