@lukoweb/apitogo 0.1.92 → 0.1.93

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/dist/cli/cli.js CHANGED
@@ -301,23 +301,15 @@ function firstApiInput(config2) {
301
301
  }
302
302
  return void 0;
303
303
  }
304
- function mergeAuthenticationGateway(config2) {
304
+ function normalizeGatewayPlacement(config2) {
305
305
  const authentication = asRecord2(config2.authentication);
306
306
  if (!authentication) {
307
- return void 0;
307
+ return;
308
308
  }
309
- const authGateway = asRecord2(authentication.gateway);
310
- const rootGateway = asRecord2(config2.gateway);
311
- if (!authGateway && !rootGateway) {
312
- return authentication;
309
+ if ("gateway" in authentication) {
310
+ delete authentication.gateway;
311
+ config2.authentication = authentication;
313
312
  }
314
- return {
315
- ...authentication,
316
- gateway: {
317
- ...rootGateway,
318
- ...authGateway
319
- }
320
- };
321
313
  }
322
314
  function ensureModules(config2) {
323
315
  const modules = asRecord2(config2.modules) ?? {};
@@ -378,11 +370,10 @@ function normalizeApitogoConfig(config2) {
378
370
  };
379
371
  }
380
372
  normalized.site = site;
381
- const authentication = mergeAuthenticationGateway(normalized);
373
+ const authentication = asRecord2(normalized.authentication);
382
374
  if (authentication) {
383
- normalized.authentication = authentication;
375
+ normalizeGatewayPlacement(normalized);
384
376
  }
385
- delete normalized.gateway;
386
377
  const modules = ensureModules(normalized);
387
378
  const legacyDocs = asRecord2(normalized.docs);
388
379
  const moduleDocs = asRecord2(modules.docs) ?? {};
@@ -533,13 +524,16 @@ function deriveManifestFields(config2) {
533
524
  continue;
534
525
  }
535
526
  if (key === "authentication") {
536
- const authentication2 = asRecord2(normalized.authentication);
537
- if (authentication2) {
538
- manifest.authentication = stripAuthenticationSecrets2(authentication2);
527
+ const authentication = asRecord2(normalized.authentication);
528
+ if (authentication) {
529
+ manifest.authentication = stripAuthenticationSecrets2(authentication);
539
530
  }
540
531
  continue;
541
532
  }
542
533
  if (key === "gateway") {
534
+ if (normalized.gateway !== void 0) {
535
+ manifest.gateway = normalized.gateway;
536
+ }
543
537
  continue;
544
538
  }
545
539
  if (key === "pricing") {
@@ -570,10 +564,6 @@ function deriveManifestFields(config2) {
570
564
  manifest[key] = normalized[key];
571
565
  }
572
566
  }
573
- const authentication = asRecord2(normalized.authentication);
574
- if (authentication?.gateway) {
575
- manifest.gateway = authentication.gateway;
576
- }
577
567
  return manifest;
578
568
  }
579
569
  function extractManifest(config2) {
@@ -1158,7 +1148,11 @@ var init_local_manifest = __esm({
1158
1148
  oidcAllowedIssuers: z4.array(z4.string()).optional(),
1159
1149
  oidcAudiences: z4.array(z4.string()).optional(),
1160
1150
  limitsJson: z4.string().optional(),
1161
- publicHealthActionKey: z4.string().optional()
1151
+ publicHealthActionKey: z4.string().optional(),
1152
+ publicRoutes: z4.array(z4.string()).optional(),
1153
+ cors: z4.object({
1154
+ allowedOrigins: z4.array(z4.string()).optional()
1155
+ }).optional()
1162
1156
  }).optional(),
1163
1157
  projectId: z4.string().optional(),
1164
1158
  landing: z4.object({
@@ -6034,6 +6028,7 @@ function manifestFilePath(siteDirectory) {
6034
6028
  var STORED_MANIFEST_KEYS = [
6035
6029
  "authentication",
6036
6030
  "backend",
6031
+ "gateway",
6037
6032
  "projectId"
6038
6033
  ];
6039
6034
  function readPlanItemsFromConfig(config2) {
@@ -6189,12 +6184,6 @@ function extractManifestFromConfig(config2) {
6189
6184
  if (readPricingEnabledFromConfig(config2)) {
6190
6185
  manifest.pricing = { enabled: true };
6191
6186
  }
6192
- const authConfig = config2.authentication;
6193
- if (authConfig?.gateway) {
6194
- manifest.gateway = authConfig.gateway;
6195
- } else if (config2.gateway) {
6196
- manifest.gateway = config2.gateway;
6197
- }
6198
6187
  const landing = readLandingFromConfig(config2);
6199
6188
  if (landing) {
6200
6189
  manifest.landing = landing;
@@ -6234,15 +6223,14 @@ function applyManifestToConfig(config2, manifest) {
6234
6223
  }
6235
6224
  delete next.plans;
6236
6225
  delete next.pricing;
6237
- if (manifest.gateway) {
6238
- const authentication = next.authentication ?? {};
6239
- authentication.gateway = manifest.gateway;
6226
+ if (next.authentication && typeof next.authentication === "object") {
6227
+ const authentication = { ...next.authentication };
6228
+ delete authentication.gateway;
6240
6229
  next.authentication = authentication;
6241
6230
  }
6242
6231
  delete next.siteName;
6243
6232
  delete next.branding;
6244
6233
  delete next.projectName;
6245
- delete next.gateway;
6246
6234
  return next;
6247
6235
  }
6248
6236
  async function readLegacyManifest(_siteDirectory) {
@@ -12545,7 +12533,7 @@ import {
12545
12533
  // package.json
12546
12534
  var package_default = {
12547
12535
  name: "@lukoweb/apitogo",
12548
- version: "0.1.92",
12536
+ version: "0.1.93",
12549
12537
  type: "module",
12550
12538
  sideEffects: [
12551
12539
  "**/*.css",
@@ -58,6 +58,9 @@ export interface DevPortalLocalManifest {
58
58
  limitsJson?: string;
59
59
  publicHealthActionKey?: string;
60
60
  publicRoutes?: string[];
61
+ cors?: {
62
+ allowedOrigins?: string[];
63
+ };
61
64
  };
62
65
  projectId?: string;
63
66
  landing?: LandingManifestInput;
@@ -89,6 +89,10 @@ export declare const DevPortalLocalManifestSchema: z.ZodObject<{
89
89
  oidcAudiences: z.ZodOptional<z.ZodArray<z.ZodString>>;
90
90
  limitsJson: z.ZodOptional<z.ZodString>;
91
91
  publicHealthActionKey: z.ZodOptional<z.ZodString>;
92
+ publicRoutes: z.ZodOptional<z.ZodArray<z.ZodString>>;
93
+ cors: z.ZodOptional<z.ZodObject<{
94
+ allowedOrigins: z.ZodOptional<z.ZodArray<z.ZodString>>;
95
+ }, z.core.$strip>>;
92
96
  }, z.core.$strip>>;
93
97
  projectId: z.ZodOptional<z.ZodString>;
94
98
  landing: z.ZodOptional<z.ZodObject<{
@@ -25,10 +25,17 @@ metadata, with optional `.env` overrides for secrets and local tweaks.
25
25
  "authentication": {
26
26
  "enabled": true,
27
27
  "type": "dev-portal",
28
- "apiBaseUrl": "https://your-dev-portal-api.example.com",
29
- "gateway": {
30
- "authMode": "bringYourOwnOidc",
31
- "oidcMetadataUrl": "https://login.example.com/.well-known/openid-configuration"
28
+ "apiBaseUrl": "https://your-dev-portal-api.example.com"
29
+ },
30
+ "gateway": {
31
+ "authMode": "bringYourOwnOidc",
32
+ "oidcMetadataUrl": "https://login.example.com/.well-known/openid-configuration",
33
+ "publicRoutes": ["/health"],
34
+ "cors": {
35
+ "allowedOrigins": [
36
+ "https://myapp.example.com",
37
+ "http://localhost:5173"
38
+ ]
32
39
  }
33
40
  },
34
41
  "modules": {
@@ -60,6 +67,36 @@ metadata, with optional `.env` overrides for secrets and local tweaks.
60
67
  `modules.userPanel.plans.items`. Add plans with `apitogo config` or edit the manifest by hand.
61
68
  See [Gateway plans](./plans.md). Legacy root `plans` is migrated on load.
62
69
  - **Docs / API keys:** configure under `modules.docs` and `modules.userPanel.apiKeys` only.
70
+ - **Gateway:** top-level `gateway` (not under `authentication`). See [Gateway CORS](#gateway-cors)
71
+ and [Gateway plans](./plans.md).
72
+
73
+ ## Gateway CORS
74
+
75
+ Browser calls from your developer portal (and any other SPA) to the API gateway are cross-origin.
76
+ APItoGo always allows the portal site origin. Add extra origins under `gateway.cors.allowedOrigins`
77
+ when tenant apps or local frontends call the gateway:
78
+
79
+ ```json
80
+ {
81
+ "gateway": {
82
+ "cors": {
83
+ "allowedOrigins": [
84
+ "https://myapp.example.com",
85
+ "http://localhost:5173"
86
+ ]
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ | Rule | Detail |
93
+ | ---- | ------ |
94
+ | Portal origin | Always allowed by the platform (`https://{your-portal-host}`). Do not list it here. |
95
+ | Extra origins | Absolute `http://` or `https://` origins only — no path, query, or fragment. |
96
+ | Wildcard | `"*"` is allowed (replaces the list; credentials stay off). |
97
+ | Empty / omitted | Portal playground still works; only extras are missing. |
98
+
99
+ Apply with `apitogo apply` / `publish_dev_portal` so APIM policies pick up the change.
63
100
 
64
101
  ## Environment variables
65
102
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.1.92",
3
+ "version": "0.1.93",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -297,28 +297,17 @@ function firstApiInput(config: ApitogoJsonConfig): string | undefined {
297
297
  return undefined;
298
298
  }
299
299
 
300
- function mergeAuthenticationGateway(
301
- config: ApitogoJsonConfig,
302
- ): Record<string, unknown> | undefined {
300
+ /** Keep gateway at config root; strip any nested authentication.gateway. */
301
+ function normalizeGatewayPlacement(config: ApitogoJsonConfig): void {
303
302
  const authentication = asRecord(config.authentication);
304
303
  if (!authentication) {
305
- return undefined;
304
+ return;
306
305
  }
307
306
 
308
- const authGateway = asRecord(authentication.gateway);
309
- const rootGateway = asRecord(config.gateway);
310
-
311
- if (!authGateway && !rootGateway) {
312
- return authentication;
307
+ if ("gateway" in authentication) {
308
+ delete authentication.gateway;
309
+ config.authentication = authentication;
313
310
  }
314
-
315
- return {
316
- ...authentication,
317
- gateway: {
318
- ...rootGateway,
319
- ...authGateway,
320
- },
321
- };
322
311
  }
323
312
 
324
313
  function ensureModules(config: ApitogoJsonConfig): Record<string, unknown> {
@@ -403,11 +392,10 @@ export function normalizeApitogoConfig(
403
392
  }
404
393
  normalized.site = site;
405
394
 
406
- const authentication = mergeAuthenticationGateway(normalized);
395
+ const authentication = asRecord(normalized.authentication);
407
396
  if (authentication) {
408
- normalized.authentication = authentication;
397
+ normalizeGatewayPlacement(normalized);
409
398
  }
410
- delete normalized.gateway;
411
399
 
412
400
  const modules = ensureModules(normalized);
413
401
  const legacyDocs = asRecord(normalized.docs);
@@ -654,6 +642,9 @@ export function deriveManifestFields(
654
642
  continue;
655
643
  }
656
644
  if (key === "gateway") {
645
+ if (normalized.gateway !== undefined) {
646
+ manifest.gateway = normalized.gateway;
647
+ }
657
648
  continue;
658
649
  }
659
650
  if (key === "pricing") {
@@ -686,11 +677,6 @@ export function deriveManifestFields(
686
677
  }
687
678
  }
688
679
 
689
- const authentication = asRecord(normalized.authentication);
690
- if (authentication?.gateway) {
691
- manifest.gateway = authentication.gateway;
692
- }
693
-
694
680
  return manifest;
695
681
  }
696
682
 
@@ -140,6 +140,12 @@ export const DevPortalLocalManifestSchema = z.object({
140
140
  oidcAudiences: z.array(z.string()).optional(),
141
141
  limitsJson: z.string().optional(),
142
142
  publicHealthActionKey: z.string().optional(),
143
+ publicRoutes: z.array(z.string()).optional(),
144
+ cors: z
145
+ .object({
146
+ allowedOrigins: z.array(z.string()).optional(),
147
+ })
148
+ .optional(),
143
149
  })
144
150
  .optional(),
145
151
  projectId: z.string().optional(),