@gyldendalas/gyldendal-divine-api 1.1.7 → 1.1.9

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/README.md CHANGED
@@ -42,4 +42,98 @@ is issued for that name.
42
42
  | polly | `http://127.0.0.1:3200` | `systime-appear-polly` docker-compose-development; its container port 3000 is taken by nuxt dev. Synthesis needs AWS credentials, and the container's CORS preflight currently rejects the `Authorization` header this SDK sends |
43
43
  | quiz (Gale CMS) | `https://galecms.test.tibalo.dk/api` | no local Gale, shared test instance |
44
44
 
45
- Run a service on a different port by passing `serviceUrl` to its constructor.
45
+ Gale also has a QA installation, which is not an environment of its own —
46
+ `quiz` is the only service that has one. It is exported as `GALE_QA_URL` and
47
+ applied as an override, so the URL stays in this package:
48
+
49
+ ```ts
50
+ import { GALE_QA_URL } from '@gyldendalas/gyldendal-divine-api';
51
+
52
+ environmentOverrides: { quiz: GALE_QA_URL }
53
+ ```
54
+
55
+ TYPO3 asks for it per site through `site.sso.sso_overrides.galeQaEndpointEnabled`.
56
+ It is a testing arrangement, so the caller decides whether to honour it: a
57
+ constructor override is applied in production too, unlike the ambient ones
58
+ below.
59
+
60
+ ### Running a mix of local and hosted services
61
+
62
+ `environment` picks one environment for everything, which is rarely what
63
+ local development wants: usually one service runs on your machine and the
64
+ rest should come from staging. `environmentOverrides` says which environment
65
+ an individual service resolves in, keyed by the service names in
66
+ `src/services/service_urls.ts`:
67
+
68
+ ```ts
69
+ const divine = {
70
+ environment, // the installationpurpose, as always
71
+ myAccountId,
72
+ environmentOverrides: { highlight: 'local' }
73
+ };
74
+
75
+ new HighlightService(divine); // http://127.0.0.1:4120
76
+ new TaggingService(divine); // https://staging-tagging.services.systime.dk
77
+ ```
78
+
79
+ Build that object once where the application starts and hand the same one to
80
+ every service, so the mix lives in a single place.
81
+
82
+ Note that it names an *environment*, not a URL. The override keeps reading
83
+ the table, so a service that moves — including the local port of the very
84
+ service you overrode — still moves for you on the next version of this
85
+ package. That is the whole point of keeping the URLs in here, and a caller
86
+ who writes a URL down has stepped out of it.
87
+
88
+ For the endpoints the table cannot know about — a service on a non-standard
89
+ port, a tunnel, a colleague's branch deploy — an override may be a URL
90
+ instead:
91
+
92
+ ```ts
93
+ environmentOverrides: { highlight: 'http://127.0.0.1:9999' }
94
+ ```
95
+
96
+ `serviceUrl` still works and still beats everything else. It is the right
97
+ tool for a service this package does not know at all, and the wrong one for
98
+ local development, where it silently opts you out of every future change to
99
+ that service's address.
100
+
101
+ ### Overrides without touching the application
102
+
103
+ An override can also come from the machine rather than from the code, which
104
+ keeps it out of the repository entirely. Per service, either:
105
+
106
+ - the environment variable `DIVINE_ENV_<SERVICE>` — `DIVINE_ENV_HIGHLIGHT`,
107
+ `DIVINE_ENV_PDF_GENERATOR`, and so on;
108
+ - `globalThis.__divineEnvironmentOverrides`, set by a dev-only Nuxt plugin or
109
+ an injected script, which is the only one of the two that reaches the
110
+ browser bundle.
111
+
112
+ Both take the same values as `environmentOverrides`. An `environmentOverrides`
113
+ entry in the code beats them, the environment variable beats `globalThis`,
114
+ and neither is read at all when `environment` is `production`: configuration
115
+ that arrives out of band is a hazard there whatever it happens to say.
116
+
117
+ ### Every override is reported
118
+
119
+ An applied override — from any of the four mechanisms, `serviceUrl`
120
+ included — writes a line like this the first time it resolves:
121
+
122
+ ```
123
+ [gyldendal-divine-api] highlight is overridden: resolved to http://127.0.0.1:4120 instead of its "development" endpoint (source: environmentOverrides).
124
+ ```
125
+
126
+ The failure mode all of this has is a forgotten override: an entry that
127
+ reached a release, or a variable exported three weeks ago and since
128
+ forgotten. So the notice is not silenced in production, where it matters
129
+ most, and it names the source, because "highlight is on 127.0.0.1" is a
130
+ puzzle on its own and stops being one as soon as it says which knob did it.
131
+
132
+ It is written once per process per distinct override, not once per request,
133
+ since a warning on every call is a warning nobody reads. Pass `onOverride` to
134
+ send it somewhere other than `console.warn`, which this package writes to
135
+ nowhere else:
136
+
137
+ ```ts
138
+ new HighlightService({ ...divine, onOverride: ({ message }) => logger.warn(message) });
139
+ ```
package/dist/index.cjs CHANGED
@@ -45,6 +45,206 @@ function extractJWTPayload(token) {
45
45
  return payload;
46
46
  }
47
47
  //#endregion
48
+ //#region src/services/service_urls.ts
49
+ /**
50
+ * The `installationpurpose` values a caller may pass as `environment`, which
51
+ * is more values than there are places to point them at.
52
+ */
53
+ const ENVIRONMENTS = [
54
+ "production",
55
+ "development",
56
+ "testing",
57
+ "local",
58
+ "test"
59
+ ];
60
+ function isServiceEnvironment(value) {
61
+ return ENVIRONMENTS.includes(value);
62
+ }
63
+ const TARGETS = {
64
+ production: "production",
65
+ development: "staging",
66
+ testing: "staging",
67
+ local: "local",
68
+ test: "mock"
69
+ };
70
+ /**
71
+ * The hosted hostnames are written out rather than derived from the service
72
+ * name: `solr-proxy-staging.eu-west-1`, `ai-bot-service-staging.eu-west-1`
73
+ * and the whole of `quiz` already break whatever pattern one would derive,
74
+ * and a wrong host is worth more than the repetition costs.
75
+ *
76
+ * The local ports are the reserved 4110-4180 block documented in the README.
77
+ */
78
+ const SERVICE_URLS = {
79
+ userSettings: {
80
+ production: (d) => `https://user-settings-service.services.${d}`,
81
+ staging: (d) => `https://staging-user-settings-service.services.${d}`,
82
+ local: () => "http://127.0.0.1:4110",
83
+ mock: () => "https://localhost:3010/services/usersettingsservice"
84
+ },
85
+ highlight: {
86
+ production: (d) => `https://highlights.services.${d}`,
87
+ staging: (d) => `https://staging-highlights.services.${d}`,
88
+ local: () => "http://127.0.0.1:4120",
89
+ mock: () => "https://localhost:3010/services/highlight"
90
+ },
91
+ pdfGenerator: {
92
+ production: (d) => `https://pdfgenerator.services.${d}`,
93
+ staging: (d) => `https://staging-pdfgenerator.services.${d}`,
94
+ local: () => "http://127.0.0.1:4130",
95
+ mock: () => "https://localhost:3010/services/pdfgenerator"
96
+ },
97
+ writingTask: {
98
+ production: (d) => `https://writingtask.services.${d}`,
99
+ staging: (d) => `https://staging-writingtask.services.${d}`,
100
+ local: () => "http://127.0.0.1:4140",
101
+ mock: () => "https://localhost:3010/services/writingtask"
102
+ },
103
+ solrProxy: {
104
+ production: (d) => `https://solr-proxy.eu-west-1.${d}`,
105
+ staging: (d) => `https://solr-proxy-staging.eu-west-1.${d}`,
106
+ local: () => "http://127.0.0.1:4150",
107
+ mock: () => "https://localhost:3010/services/solrproxy"
108
+ },
109
+ tagging: {
110
+ production: (d) => `https://tagging.services.${d}`,
111
+ staging: (d) => `https://staging-tagging.services.${d}`,
112
+ local: () => "http://127.0.0.1:4160",
113
+ mock: () => "https://localhost:3010/services/tagging"
114
+ },
115
+ aiBot: {
116
+ production: (d) => `https://ai-bot-service.eu-west-1.${d}`,
117
+ staging: (d) => `https://ai-bot-service-staging.eu-west-1.${d}`,
118
+ local: () => "http://127.0.0.1:4170",
119
+ mock: () => "https://localhost:3010/services/aibotservice"
120
+ },
121
+ cookieConsentLog: {
122
+ production: (d) => `https://cookieconsentlog.services.${d}`,
123
+ staging: (d) => `https://staging-cookieconsentlog.services.${d}`,
124
+ local: () => "http://127.0.0.1:4180",
125
+ mock: () => "https://localhost:3010/services/cookieconsentlog"
126
+ },
127
+ polly: {
128
+ production: (d) => `https://appear-polly.services.${d}`,
129
+ staging: (d) => `https://staging-appear-polly.services.${d}`,
130
+ local: () => "http://127.0.0.1:3200",
131
+ mock: () => "https://localhost:3010/services/polly"
132
+ },
133
+ quiz: {
134
+ production: () => "https://api.iquiz.dk/api",
135
+ staging: () => "https://galecms.test.tibalo.dk/api",
136
+ local: () => "https://galecms.test.tibalo.dk/api",
137
+ mock: () => "https://localhost:3010/galeapi/api"
138
+ }
139
+ };
140
+ /**
141
+ * Gale CMS's QA installation, which is not a target of its own: `quiz` is the
142
+ * only service with a QA endpoint, and a fifth `Target` would oblige the other
143
+ * nine to name one they do not have.
144
+ *
145
+ * It is reached by overriding `quiz` with it, so the URL stays in this file
146
+ * and a caller who turns it on keeps tracking this package:
147
+ *
148
+ * ```ts
149
+ * environmentOverrides: { quiz: GALE_QA_URL }
150
+ * ```
151
+ *
152
+ * TYPO3 asks for it through `site.sso.sso_overrides.galeQaEndpointEnabled`,
153
+ * which is a testing arrangement: honour it outside production only.
154
+ */
155
+ const GALE_QA_URL = "https://galecms.qa.tibalo.dk/api";
156
+ /** Where `service` answers in `environment`. */
157
+ function resolveServiceUrl(service, environment, baseDomain) {
158
+ if (!isServiceEnvironment(environment)) throw new Error(`Unknown environment: ${environment}`);
159
+ return SERVICE_URLS[service][TARGETS[environment]](baseDomain);
160
+ }
161
+ //#endregion
162
+ //#region src/services/service_overrides.ts
163
+ /**
164
+ * Pointing one service somewhere else than the rest.
165
+ *
166
+ * A caller developing locally usually wants most services from staging and
167
+ * one from their own machine. The override says which *environment* that one
168
+ * service resolves in, not which URL it answers on, so the caller keeps
169
+ * tracking `service_urls.ts` for every service they did not name — and for
170
+ * the named one too, whenever its local port moves.
171
+ *
172
+ * A URL is still accepted for the cases the table cannot know about (a
173
+ * service on a non-standard port, a tunnel, a colleague's branch deploy).
174
+ *
175
+ * Every override is reported, because the failure mode of all of this is a
176
+ * forgotten one: an `environmentOverrides` entry that reached a release, or
177
+ * an environment variable exported three weeks ago.
178
+ */
179
+ /**
180
+ * Set by a dev-only Nuxt plugin or an injected script, and the only way to
181
+ * reach the browser bundle, where there is no `process.env`.
182
+ */
183
+ const GLOBAL_OVERRIDES_KEY = "__divineEnvironmentOverrides";
184
+ /** `pdfGenerator` is configured as `DIVINE_ENV_PDF_GENERATOR`. */
185
+ function overrideEnvVarName(service) {
186
+ return `DIVINE_ENV_${service.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase()}`;
187
+ }
188
+ /**
189
+ * An override the developer's machine carries rather than the application's
190
+ * code. `process.env` is read through a computed key so a bundler treats it
191
+ * as a lookup rather than inlining a build-time value, and both accesses are
192
+ * guarded because neither global exists everywhere this package runs.
193
+ *
194
+ * The environment variable wins: it is set per run of the application, so it
195
+ * is the more deliberate of the two.
196
+ */
197
+ function ambientOverride(service) {
198
+ const fromEnv = typeof process !== "undefined" && process.env ? process.env[overrideEnvVarName(service)] : void 0;
199
+ if (fromEnv) return {
200
+ value: fromEnv,
201
+ source: "env"
202
+ };
203
+ const fromGlobal = globalThis[GLOBAL_OVERRIDES_KEY]?.[service];
204
+ if (fromGlobal) return {
205
+ value: fromGlobal,
206
+ source: "globalThis"
207
+ };
208
+ }
209
+ /** An override that is not an environment name has to be a URL we can call. */
210
+ function overrideToUrl(value, service, source) {
211
+ if (/^https?:\/\//.test(value)) return value;
212
+ throw new Error(`Invalid ${source} override for ${service}: "${value}" is neither an environment (${ENVIRONMENTS.join(", ")}) nor an http(s) URL.`);
213
+ }
214
+ /**
215
+ * Already-reported overrides, so a warning survives being read.
216
+ * `getUrlPrefix()` runs on every request, and an override that warns on every
217
+ * one of them is an override nobody reads.
218
+ *
219
+ * This is process-wide mutable state, which is exactly what a per-request
220
+ * `environment` must never be kept in — but it only ever suppresses output,
221
+ * and "once per process" is the cadence a server operator wants anyway.
222
+ */
223
+ const reported = /* @__PURE__ */ new Set();
224
+ /** Test seam: forget what has already been reported. */
225
+ function resetOverrideReports() {
226
+ reported.clear();
227
+ }
228
+ const defaultReporter = (details) => console.warn(details.message);
229
+ /**
230
+ * Report an override the first time it resolves. Deliberately not silenced in
231
+ * production: an `environmentOverrides` entry that reached a release is the
232
+ * single thing this is here to catch.
233
+ */
234
+ function reportOverride({ service, environment, resolved, source }, onOverride) {
235
+ const key = `${source}:${service}:${resolved}`;
236
+ if (reported.has(key)) return;
237
+ reported.add(key);
238
+ const message = `[gyldendal-divine-api] ${service} is overridden: resolved to ${resolved} instead of its "${environment}" endpoint (source: ${source}).`;
239
+ (onOverride ?? defaultReporter)({
240
+ service,
241
+ environment,
242
+ resolved,
243
+ source,
244
+ message
245
+ });
246
+ }
247
+ //#endregion
48
248
  //#region src/services/base_service.ts
49
249
  var HTTPError = class extends Error {
50
250
  response;
@@ -61,19 +261,69 @@ var BaseService = class {
61
261
  environment;
62
262
  myAccountId;
63
263
  baseDomain;
64
- constructor({ bearerToken = void 0, apiKey = void 0, serviceUrl = void 0, environment = "production", myAccountId = "SYSTIMEMYACCOUNT", baseDomain = "systime.dk" }) {
264
+ environmentOverrides;
265
+ onOverride;
266
+ /**
267
+ * This service's entry in `SERVICE_URLS`, declared by every service in this
268
+ * package. A subclass of our own that declares none resolves nothing from
269
+ * the table and has to be given a `serviceUrl`.
270
+ */
271
+ serviceName;
272
+ constructor({ bearerToken = void 0, apiKey = void 0, serviceUrl = void 0, environment = "production", myAccountId = "SYSTIMEMYACCOUNT", baseDomain = "systime.dk", environmentOverrides = void 0, onOverride = void 0 }) {
65
273
  this.bearerToken = bearerToken;
66
274
  this.apiKey = apiKey;
67
275
  this.serviceUrl = serviceUrl;
68
276
  this.environment = environment.toLowerCase();
69
277
  this.myAccountId = myAccountId;
70
278
  this.baseDomain = baseDomain;
279
+ this.environmentOverrides = environmentOverrides;
280
+ this.onOverride = onOverride;
71
281
  }
282
+ /**
283
+ * The override that applies to this service, if any. An explicit
284
+ * `environmentOverrides` entry beats the developer's ambient configuration,
285
+ * and ambient configuration is not read at all in production: config that
286
+ * arrives out of band is a hazard there whatever it happens to say.
287
+ */
288
+ findOverride() {
289
+ if (!this.serviceName) return;
290
+ const explicit = this.environmentOverrides?.[this.serviceName];
291
+ if (explicit) return {
292
+ value: explicit,
293
+ source: "environmentOverrides"
294
+ };
295
+ if (this.environment === "production") return;
296
+ return ambientOverride(this.serviceName);
297
+ }
298
+ /**
299
+ * Where this service answers, for the environment it was constructed with
300
+ * or for the one an override names in its place. Ignores `serviceUrl`;
301
+ * `getUrlPrefix()` is what honours that.
302
+ */
72
303
  discoverUrlPrefix() {
73
- throw new Error("Method not implemented.");
304
+ const service = this.serviceName;
305
+ if (!service) throw new Error("Method not implemented.");
306
+ const override = this.findOverride();
307
+ if (!override) return resolveServiceUrl(service, this.environment, this.baseDomain);
308
+ const resolved = isServiceEnvironment(override.value) ? resolveServiceUrl(service, override.value, this.baseDomain) : overrideToUrl(override.value, service, override.source);
309
+ reportOverride({
310
+ service,
311
+ environment: this.environment,
312
+ resolved,
313
+ source: override.source
314
+ }, this.onOverride);
315
+ return resolved;
74
316
  }
75
317
  getUrlPrefix() {
76
- if (this.serviceUrl) return this.serviceUrl;
318
+ if (this.serviceUrl) {
319
+ if (this.serviceName) reportOverride({
320
+ service: this.serviceName,
321
+ environment: this.environment,
322
+ resolved: this.serviceUrl,
323
+ source: "serviceUrl"
324
+ }, this.onOverride);
325
+ return this.serviceUrl;
326
+ }
77
327
  return this.discoverUrlPrefix();
78
328
  }
79
329
  async addAuthHeaders(requestHeaders) {
@@ -147,16 +397,7 @@ var BaseService = class {
147
397
  //#endregion
148
398
  //#region src/services/tagging_service.ts
149
399
  var TaggingService = class extends BaseService {
150
- discoverUrlPrefix() {
151
- switch (this.environment) {
152
- case "production": return `https://tagging.services.${this.baseDomain}`;
153
- case "development":
154
- case "testing": return `https://staging-tagging.services.${this.baseDomain}`;
155
- case "local": return `http://127.0.0.1:4160`;
156
- case "test": return `https://localhost:3010/services/tagging`;
157
- default: throw new Error(`Unknown environment: ${this.environment}`);
158
- }
159
- }
400
+ serviceName = "tagging";
160
401
  async getTagsByResource({ identity, resource_type, resource_name, tag_name, timeout = 3e3 }) {
161
402
  const url = `${this.getUrlPrefix()}/tags/by_resource/${identity}${resource_type ? `/${resource_type}` : ""}${resource_name ? `/${resource_name}` : ""}${tag_name ? `/${tag_name}` : ""}`;
162
403
  const headers = new Headers();
@@ -218,16 +459,7 @@ let SSMLSpeechSpeeds = /* @__PURE__ */ function(SSMLSpeechSpeeds) {
218
459
  return SSMLSpeechSpeeds;
219
460
  }({});
220
461
  var PollyService = class extends BaseService {
221
- discoverUrlPrefix() {
222
- switch (this.environment) {
223
- case "production": return `https://appear-polly.services.${this.baseDomain}`;
224
- case "development":
225
- case "testing": return `https://staging-appear-polly.services.${this.baseDomain}`;
226
- case "local": return `http://127.0.0.1:3200`;
227
- case "test": return `https://localhost:3010/services/polly`;
228
- default: throw new Error(`Unknown environment: ${this.environment}`);
229
- }
230
- }
462
+ serviceName = "polly";
231
463
  async synthesize({ body, timeout = 3e3 }) {
232
464
  const url = `${this.getUrlPrefix()}/synthesizeSpeech`;
233
465
  const headers = new Headers();
@@ -243,16 +475,7 @@ var PollyService = class extends BaseService {
243
475
  //#endregion
244
476
  //#region src/services/cookie_consent_log.ts
245
477
  var CookieConsentLog = class extends BaseService {
246
- discoverUrlPrefix() {
247
- switch (this.environment) {
248
- case "production": return `https://cookieconsentlog.services.${this.baseDomain}`;
249
- case "development":
250
- case "testing": return `https://staging-cookieconsentlog.services.${this.baseDomain}`;
251
- case "local": return `http://127.0.0.1:4180`;
252
- case "test": return `https://localhost:3010/services/cookieconsentlog`;
253
- default: throw new Error(`Unknown environment: ${this.environment}`);
254
- }
255
- }
478
+ serviceName = "cookieConsentLog";
256
479
  async logCookieConsent({ body, timeout = 3e3 }) {
257
480
  const url = `${this.getUrlPrefix()}/log`;
258
481
  const headers = new Headers();
@@ -274,16 +497,7 @@ let HighlightResultType = /* @__PURE__ */ function(HighlightResultType) {
274
497
  return HighlightResultType;
275
498
  }({});
276
499
  var HighlightService = class extends BaseService {
277
- discoverUrlPrefix() {
278
- switch (this.environment) {
279
- case "production": return `https://highlights.services.${this.baseDomain}`;
280
- case "development":
281
- case "testing": return `https://staging-highlights.services.${this.baseDomain}`;
282
- case "local": return `http://127.0.0.1:4120`;
283
- case "test": return `https://localhost:3010/services/highlight`;
284
- default: throw new Error(`Unknown environment: ${this.environment}`);
285
- }
286
- }
500
+ serviceName = "highlight";
287
501
  async search({ search, timeout = 3e3 }) {
288
502
  const url = `${this.getUrlPrefix()}/v2/search`;
289
503
  const headers = new Headers();
@@ -355,16 +569,7 @@ var HighlightService = class extends BaseService {
355
569
  //#endregion
356
570
  //#region src/services/user_settings_base.ts
357
571
  var UserSettingsBase = class extends BaseService {
358
- discoverUrlPrefix() {
359
- switch (this.environment) {
360
- case "production": return `https://user-settings-service.services.${this.baseDomain}`;
361
- case "development":
362
- case "testing": return `https://staging-user-settings-service.services.${this.baseDomain}`;
363
- case "local": return `http://127.0.0.1:4110`;
364
- case "test": return `https://localhost:3010/services/usersettingsservice`;
365
- default: throw new Error(`Unknown environment: ${this.environment}`);
366
- }
367
- }
572
+ serviceName = "userSettings";
368
573
  };
369
574
  //#endregion
370
575
  //#region src/services/user_settings_clientsettings.ts
@@ -527,16 +732,7 @@ var UserSettingsMRU = class extends UserSettingsBase {
527
732
  //#endregion
528
733
  //#region src/services/pdf_generator.ts
529
734
  var PdfGeneratorService = class extends BaseService {
530
- discoverUrlPrefix() {
531
- switch (this.environment) {
532
- case "production": return `https://pdfgenerator.services.${this.baseDomain}`;
533
- case "development":
534
- case "testing": return `https://staging-pdfgenerator.services.${this.baseDomain}`;
535
- case "local": return `http://127.0.0.1:4130`;
536
- case "test": return `https://localhost:3010/services/pdfgenerator`;
537
- default: throw new Error(`Unknown environment: ${this.environment}`);
538
- }
539
- }
735
+ serviceName = "pdfGenerator";
540
736
  async pdfFromSiteMap({ body, timeout = 3e3 }) {
541
737
  const url = `${this.getUrlPrefix()}/sitemap`;
542
738
  const headers = new Headers();
@@ -878,16 +1074,7 @@ var UserFolders = class extends TaggingService {
878
1074
  //#endregion
879
1075
  //#region src/services/ai_bot_service.ts
880
1076
  var AIBotService = class extends BaseService {
881
- discoverUrlPrefix() {
882
- switch (this.environment) {
883
- case "production": return `https://ai-bot-service.eu-west-1.${this.baseDomain}`;
884
- case "development":
885
- case "testing": return `https://ai-bot-service-staging.eu-west-1.${this.baseDomain}`;
886
- case "local": return `http://127.0.0.1:4170`;
887
- case "test": return `https://localhost:3010/services/aibotservice`;
888
- default: throw new Error(`Unknown environment: ${this.environment}`);
889
- }
890
- }
1077
+ serviceName = "aiBot";
891
1078
  async constructChatUrl({ interactivityId, isbn, stream }) {
892
1079
  if (interactivityId && isbn) throw new Error("interactivityId and isbn are mutually exclusive");
893
1080
  if (!interactivityId && !isbn) throw new Error("Either interactivityId or isbn must be provided");
@@ -934,15 +1121,23 @@ var AIBotService = class extends BaseService {
934
1121
  //#region src/services/quiz_service.ts
935
1122
  const REQUEST_TIMEOUT = 1e4;
936
1123
  var QuizService = class extends BaseService {
937
- discoverUrlPrefix() {
938
- switch (this.environment) {
939
- case "production": return "https://api.iquiz.dk/api";
940
- case "development":
941
- case "testing":
942
- case "local": return "https://galecms.test.tibalo.dk/api";
943
- case "test": return `https://localhost:3010/galeapi/api`;
944
- default: throw new Error(`Unknown environment: ${this.environment}`);
945
- }
1124
+ serviceName = "quiz";
1125
+ /**
1126
+ * Where a mediafile `identifier` is served from: the API prefix without its
1127
+ * path, so the host mapping stays in `SERVICE_URLS` alone and everything
1128
+ * that can redirect the API — an explicit `serviceUrl`, a per-service
1129
+ * override — carries the media with it.
1130
+ *
1131
+ * The exception is the mock server, where the app serves the fixtures
1132
+ * itself and their identifiers are already relative to it. That is asked of
1133
+ * the resolved prefix rather than of `environment`, so it stays true when a
1134
+ * caller is sent to the mock by an override, and stops being true when one
1135
+ * sends them somewhere real from the mock environment.
1136
+ */
1137
+ getMediaUrlPrefix() {
1138
+ const prefix = this.getUrlPrefix();
1139
+ if (prefix === resolveServiceUrl("quiz", "test", this.baseDomain)) return "";
1140
+ return new URL(prefix).origin;
946
1141
  }
947
1142
  makeHeaders(extra) {
948
1143
  const headers = new Headers();
@@ -1158,16 +1353,7 @@ var QuizService = class extends BaseService {
1158
1353
  * token as the highlight service does.
1159
1354
  */
1160
1355
  var WritingTaskService = class extends BaseService {
1161
- discoverUrlPrefix() {
1162
- switch (this.environment) {
1163
- case "production": return `https://writingtask.services.${this.baseDomain}`;
1164
- case "development":
1165
- case "testing": return `https://staging-writingtask.services.${this.baseDomain}`;
1166
- case "local": return `http://127.0.0.1:4140`;
1167
- case "test": return `https://localhost:3010/services/writingtask`;
1168
- default: throw new Error(`Unknown environment: ${this.environment}`);
1169
- }
1170
- }
1356
+ serviceName = "writingTask";
1171
1357
  /** Answers stored for the given content ids, one bucket per cid. */
1172
1358
  async getResponse({ userId, isbn, pid, cids, timeout = 5e3 }) {
1173
1359
  const url = `${this.getUrlPrefix()}/getResponse`;
@@ -1227,25 +1413,19 @@ var WritingTaskService = class extends BaseService {
1227
1413
  //#endregion
1228
1414
  //#region src/services/solr_proxy_service.ts
1229
1415
  var SolrProxyService = class extends BaseService {
1230
- discoverUrlPrefix() {
1231
- switch (this.environment) {
1232
- case "production": return `https://solr-proxy.eu-west-1.${this.baseDomain}`;
1233
- case "development":
1234
- case "testing": return `https://solr-proxy-staging.eu-west-1.${this.baseDomain}`;
1235
- case "local": return `http://127.0.0.1:4150`;
1236
- case "test": return `https://localhost:3010/services/solrproxy`;
1237
- default: throw new Error(`Unknown environment: ${this.environment}`);
1238
- }
1239
- }
1416
+ serviceName = "solrProxy";
1240
1417
  };
1241
1418
  //#endregion
1242
1419
  exports.AIBotService = AIBotService;
1243
1420
  exports.CookieConsentLog = CookieConsentLog;
1421
+ exports.ENVIRONMENTS = ENVIRONMENTS;
1422
+ exports.GALE_QA_URL = GALE_QA_URL;
1244
1423
  exports.HighlightResultType = HighlightResultType;
1245
1424
  exports.HighlightService = HighlightService;
1246
1425
  exports.PdfGeneratorService = PdfGeneratorService;
1247
1426
  exports.PollyService = PollyService;
1248
1427
  exports.QuizService = QuizService;
1428
+ exports.SERVICE_URLS = SERVICE_URLS;
1249
1429
  exports.SSMLSpeechSpeeds = SSMLSpeechSpeeds;
1250
1430
  exports.SolrProxyService = SolrProxyService;
1251
1431
  exports.TaggingService = TaggingService;
@@ -1255,6 +1435,13 @@ exports.UserSettingsClientSettings = UserSettingsClientSettings;
1255
1435
  exports.UserSettingsContinue = UserSettingsContinue;
1256
1436
  exports.UserSettingsMRU = UserSettingsMRU;
1257
1437
  exports.WritingTaskService = WritingTaskService;
1438
+ exports.ambientOverride = ambientOverride;
1258
1439
  exports.getJwtExpiry = getJwtExpiry;
1440
+ exports.isServiceEnvironment = isServiceEnvironment;
1441
+ exports.overrideEnvVarName = overrideEnvVarName;
1442
+ exports.overrideToUrl = overrideToUrl;
1443
+ exports.reportOverride = reportOverride;
1444
+ exports.resetOverrideReports = resetOverrideReports;
1445
+ exports.resolveServiceUrl = resolveServiceUrl;
1259
1446
 
1260
1447
  //# sourceMappingURL=index.cjs.map