@crossdelta/infrastructure 0.2.22 → 0.2.23

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.
@@ -1,18 +1,21 @@
1
- import type { ServiceConfig } from '../types';
1
+ import type { K8sServiceConfig } from '../runtimes/doks/types';
2
2
  /**
3
- * Auto-discovers all service configurations from a directory.
4
- * Each .ts file (except index.ts) should export a ServiceConfig as default.
3
+ * Auto-discovers all K8s service configurations from infra/services directory.
4
+ * Each .ts file (except index.ts) should export a K8sServiceConfig as default.
5
+ *
6
+ * If a config does not specify `image`, it will be auto-generated using
7
+ * the registry from root package.json's `pf.registry` config.
5
8
  *
6
9
  * @param servicesDir - Path to services directory (relative to cwd or absolute)
7
- * @throws Error if duplicate ports are detected
10
+ * @throws Error if duplicate ports are detected or pf.registry is missing
8
11
  *
9
12
  * @example
10
13
  * ```typescript
11
- * // Relative path (resolved from process.cwd())
12
- * const configs = discoverServices('infra/services')
13
- *
14
- * // Absolute path
15
- * const configs = discoverServices('/absolute/path/to/services')
14
+ * // Config without image - will use pf.registry automatically
15
+ * const config: K8sServiceConfig = {
16
+ * name: 'my-service',
17
+ * containerPort: 4001,
18
+ * }
16
19
  * ```
17
20
  */
18
- export declare function discoverServices(servicesDir: string): ServiceConfig[];
21
+ export declare function discoverServiceConfigs(servicesDir?: string): K8sServiceConfig[];
package/dist/index.cjs CHANGED
@@ -289067,7 +289067,7 @@ var require_lib38 = __commonJS((exports2, module2) => {
289067
289067
  var require_lib39 = __commonJS((exports2, module2) => {
289068
289068
  var fs = require("fs");
289069
289069
  var { promisify } = require("util");
289070
- var { readFileSync } = fs;
289070
+ var { readFileSync: readFileSync2 } = fs;
289071
289071
  var readFile = promisify(fs.readFile);
289072
289072
  var extractPath = (path, cmdshimContents) => {
289073
289073
  if (/[.]cmd$/.test(path)) {
@@ -289119,7 +289119,7 @@ var require_lib39 = __commonJS((exports2, module2) => {
289119
289119
  });
289120
289120
  };
289121
289121
  var readCmdShimSync = (path) => {
289122
- const contents = readFileSync(path);
289122
+ const contents = readFileSync2(path);
289123
289123
  const destination = extractPath(path, contents.toString());
289124
289124
  if (!destination) {
289125
289125
  throw notaShim(path);
@@ -308309,7 +308309,7 @@ var require_getImage = __commonJS((exports2) => {
308309
308309
  exports2.getImageOutput = exports2.getImage = undefined;
308310
308310
  var pulumi = require_pulumi();
308311
308311
  var utilities = require_utilities();
308312
- function getImage(args, opts) {
308312
+ function getImage2(args, opts) {
308313
308313
  args = args || {};
308314
308314
  opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts || {});
308315
308315
  return pulumi.runtime.invoke("digitalocean:index/getImage:getImage", {
@@ -308319,7 +308319,7 @@ var require_getImage = __commonJS((exports2) => {
308319
308319
  source: args.source
308320
308320
  }, opts);
308321
308321
  }
308322
- exports2.getImage = getImage;
308322
+ exports2.getImage = getImage2;
308323
308323
  function getImageOutput(args, opts) {
308324
308324
  args = args || {};
308325
308325
  opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts || {});
@@ -337015,12 +337015,12 @@ var exports_lib = {};
337015
337015
  __export(exports_lib, {
337016
337016
  ghcrImage: () => ghcrImage,
337017
337017
  getServiceUrl: () => getServiceUrl,
337018
- getServicePort: () => getServicePort2,
337018
+ getServicePort: () => getServicePort,
337019
337019
  getImage: () => getImage,
337020
337020
  filterByPlatform: () => filterByPlatform,
337021
337021
  ensureDot: () => ensureDot,
337022
337022
  dockerHubImage: () => dockerHubImage,
337023
- discoverServices: () => discoverServices,
337023
+ discoverServiceConfigs: () => discoverServiceConfigs,
337024
337024
  deployNginxIngress: () => deployNginxIngress,
337025
337025
  deployNats: () => deployNats,
337026
337026
  deployK8sServices: () => deployK8sServices,
@@ -337081,21 +337081,67 @@ var defaultHealthCheck = {
337081
337081
  // lib/helpers/discover-services.ts
337082
337082
  var import_node_fs = require("node:fs");
337083
337083
  var import_node_path = require("node:path");
337084
- function getServicePort(config) {
337085
- if (config.httpPort)
337086
- return config.httpPort;
337087
- const internalPorts = config.internalPorts;
337088
- if (internalPorts?.[0])
337089
- return internalPorts[0];
337090
- return 8080;
337084
+
337085
+ // lib/helpers/image.ts
337086
+ var scopeImageTagsRaw = process.env.SCOPE_IMAGE_TAGS ?? "";
337087
+ var scopeImageTags = (() => {
337088
+ if (!scopeImageTagsRaw.trim()) {
337089
+ return {};
337090
+ }
337091
+ try {
337092
+ const parsed = JSON.parse(scopeImageTagsRaw);
337093
+ const tags = {};
337094
+ for (const [key, value] of Object.entries(parsed ?? {})) {
337095
+ if (typeof value === "string" && value.trim().length > 0) {
337096
+ tags[key] = value.trim();
337097
+ }
337098
+ }
337099
+ return tags;
337100
+ } catch (error) {
337101
+ console.warn("Unable to parse scope image tags from environment:", error);
337102
+ return {};
337103
+ }
337104
+ })();
337105
+ var resolveImageTag = (scopeName) => {
337106
+ const tag = scopeImageTags[scopeName];
337107
+ if (!tag) {
337108
+ return "latest";
337109
+ }
337110
+ return tag;
337111
+ };
337112
+ var ghcrImage = (registry, serviceName) => {
337113
+ const tag = resolveImageTag(serviceName);
337114
+ return `ghcr.io/${registry}/${serviceName}:${tag}`;
337115
+ };
337116
+ var getImage = (registry, repository, registryCredentials) => {
337117
+ const scopeName = repository.split("/").pop();
337118
+ if (!scopeName) {
337119
+ throw new Error(`Invalid repository name: ${repository}`);
337120
+ }
337121
+ return {
337122
+ registryType: "GHCR",
337123
+ registry,
337124
+ repository,
337125
+ registryCredentials,
337126
+ tag: resolveImageTag(scopeName)
337127
+ };
337128
+ };
337129
+
337130
+ // lib/helpers/discover-services.ts
337131
+ function getRegistryFromPackageJson(workspaceRoot) {
337132
+ const pkgPath = import_node_path.join(workspaceRoot, "package.json");
337133
+ const pkg = JSON.parse(import_node_fs.readFileSync(pkgPath, "utf-8"));
337134
+ if (!pkg.pf?.registry) {
337135
+ throw new Error(`Missing "pf.registry" in ${pkgPath}. Add it to your root package.json, e.g.: "pf": { "registry": "orgname/reponame" }`);
337136
+ }
337137
+ return pkg.pf.registry;
337091
337138
  }
337092
337139
  function validateNoDuplicatePorts(configs) {
337093
337140
  const portMap = new Map;
337094
337141
  for (const config of configs) {
337095
- const port = getServicePort(config);
337096
- const existing = portMap.get(port) || [];
337142
+ const existing = portMap.get(config.containerPort) || [];
337097
337143
  existing.push(config.name);
337098
- portMap.set(port, existing);
337144
+ portMap.set(config.containerPort, existing);
337099
337145
  }
337100
337146
  const conflicts = [...portMap.entries()].filter(([, services]) => services.length > 1).map(([port, services]) => `Port ${port}: ${services.join(", ")}`);
337101
337147
  if (conflicts.length > 0) {
@@ -337104,13 +337150,22 @@ ${conflicts.join(`
337104
337150
  `)}`);
337105
337151
  }
337106
337152
  }
337107
- function discoverServices(servicesDir) {
337153
+ function discoverServiceConfigs(servicesDir = "infra/services") {
337108
337154
  const resolvedDir = import_node_path.isAbsolute(servicesDir) ? servicesDir : import_node_path.resolve(process.cwd(), servicesDir);
337155
+ const workspaceRoot = import_node_path.resolve(resolvedDir, "..", "..");
337156
+ const registry = getRegistryFromPackageJson(workspaceRoot);
337109
337157
  const files = import_node_fs.readdirSync(resolvedDir).filter((file) => file.endsWith(".ts") && file !== "index.ts");
337110
337158
  const configs = files.map((file) => {
337111
337159
  const filePath = import_node_path.join(resolvedDir, file);
337112
337160
  const module2 = require(filePath);
337113
- return module2.default ?? module2;
337161
+ const config = module2.default ?? module2;
337162
+ if (!config.image) {
337163
+ return {
337164
+ ...config,
337165
+ image: ghcrImage(registry, config.name)
337166
+ };
337167
+ }
337168
+ return config;
337114
337169
  });
337115
337170
  validateNoDuplicatePorts(configs);
337116
337171
  return configs;
@@ -337338,50 +337393,6 @@ function buildDropletServices(options) {
337338
337393
  function filterByPlatform(configs, platform) {
337339
337394
  return configs.filter((c) => (c.platform || "app-platform") === platform);
337340
337395
  }
337341
- // lib/helpers/image.ts
337342
- var scopeImageTagsRaw = process.env.SCOPE_IMAGE_TAGS ?? "";
337343
- var scopeImageTags = (() => {
337344
- if (!scopeImageTagsRaw.trim()) {
337345
- return {};
337346
- }
337347
- try {
337348
- const parsed = JSON.parse(scopeImageTagsRaw);
337349
- const tags = {};
337350
- for (const [key, value] of Object.entries(parsed ?? {})) {
337351
- if (typeof value === "string" && value.trim().length > 0) {
337352
- tags[key] = value.trim();
337353
- }
337354
- }
337355
- return tags;
337356
- } catch (error) {
337357
- console.warn("Unable to parse scope image tags from environment:", error);
337358
- return {};
337359
- }
337360
- })();
337361
- var resolveImageTag = (scopeName) => {
337362
- const tag = scopeImageTags[scopeName];
337363
- if (!tag) {
337364
- return "latest";
337365
- }
337366
- return tag;
337367
- };
337368
- var ghcrImage = (registry, serviceName) => {
337369
- const tag = resolveImageTag(serviceName);
337370
- return `ghcr.io/${registry}/${serviceName}:${tag}`;
337371
- };
337372
- var getImage = (registry, repository, registryCredentials) => {
337373
- const scopeName = repository.split("/").pop();
337374
- if (!scopeName) {
337375
- throw new Error(`Invalid repository name: ${repository}`);
337376
- }
337377
- return {
337378
- registryType: "GHCR",
337379
- registry,
337380
- repository,
337381
- registryCredentials,
337382
- tag: resolveImageTag(scopeName)
337383
- };
337384
- };
337385
337396
  // lib/helpers/service-builder.ts
337386
337397
  function buildServices(options) {
337387
337398
  const { serviceConfigs, registryCredentials, logtailToken, registry } = options;
@@ -337426,7 +337437,7 @@ function buildIngressRules(serviceConfigs) {
337426
337437
  }
337427
337438
  // lib/helpers/service-runtime.ts
337428
337439
  var toEnvKey = (name) => name.toUpperCase().replace(/-/g, "_");
337429
- function getServicePort2(serviceName, defaultPort = 8080) {
337440
+ function getServicePort(serviceName, defaultPort = 8080) {
337430
337441
  const envKey = `${toEnvKey(serviceName)}_PORT`;
337431
337442
  const envValue = process.env[envKey];
337432
337443
  if (envValue) {
@@ -337442,7 +337453,7 @@ function getServiceUrl(serviceName) {
337442
337453
  return process.env[envKey];
337443
337454
  }
337444
337455
  // lib/helpers/service-urls.ts
337445
- function getServicePort3(config) {
337456
+ function getServicePort2(config) {
337446
337457
  if (config.httpPort)
337447
337458
  return config.httpPort;
337448
337459
  const internalPorts = config.internalPorts;
@@ -337452,7 +337463,7 @@ function getServicePort3(config) {
337452
337463
  }
337453
337464
  function buildInternalUrls(serviceConfigs) {
337454
337465
  return Object.fromEntries(serviceConfigs.map((config) => {
337455
- const url = config.internalUrl ?? `http://${config.name}:${getServicePort3(config)}`;
337466
+ const url = config.internalUrl ?? `http://${config.name}:${getServicePort2(config)}`;
337456
337467
  return [config.name, url];
337457
337468
  }));
337458
337469
  }
@@ -337464,7 +337475,7 @@ function buildExternalUrls(serviceConfigs, baseUrl) {
337464
337475
  }
337465
337476
  function buildLocalUrls(serviceConfigs) {
337466
337477
  return Object.fromEntries(serviceConfigs.map((config) => {
337467
- const port = getServicePort3(config);
337478
+ const port = getServicePort2(config);
337468
337479
  return [config.name, `http://localhost:${port}`];
337469
337480
  }));
337470
337481
  }
@@ -337472,14 +337483,14 @@ function buildServiceUrlEnvs(serviceConfigs) {
337472
337483
  return serviceConfigs.map((config) => ({
337473
337484
  key: `${config.name.toUpperCase().replace(/-/g, "_")}_URL`,
337474
337485
  scope: "RUN_TIME",
337475
- value: config.internalUrl ?? `http://${config.name}:${getServicePort3(config)}`
337486
+ value: config.internalUrl ?? `http://${config.name}:${getServicePort2(config)}`
337476
337487
  }));
337477
337488
  }
337478
337489
  function buildServicePortEnvs(serviceConfigs) {
337479
337490
  return serviceConfigs.map((config) => ({
337480
337491
  key: `${config.name.toUpperCase().replace(/-/g, "_")}_PORT`,
337481
337492
  scope: "RUN_TIME",
337482
- value: String(getServicePort3(config))
337493
+ value: String(getServicePort2(config))
337483
337494
  }));
337484
337495
  }
337485
337496
  // lib/runtimes/doks/cert-manager.ts
@@ -337845,6 +337856,9 @@ function getImagePullPolicy(image2, explicit) {
337845
337856
  return image2.endsWith(":latest") ? "Always" : "IfNotPresent";
337846
337857
  }
337847
337858
  function deployK8sService(provider, namespace, config2) {
337859
+ if (!config2.image) {
337860
+ throw new Error(`Missing "image" for service "${config2.name}". Either specify image explicitly or use discoverServiceConfigs() which auto-generates it from pf.registry.`);
337861
+ }
337848
337862
  const labels = buildLabels(config2.name, config2.labels);
337849
337863
  const replicas = config2.replicas ?? DEFAULTS.replicas;
337850
337864
  const imagePullPolicy = getImagePullPolicy(config2.image, config2.imagePullPolicy);
package/dist/index.js CHANGED
@@ -289030,7 +289030,7 @@ var require_lib38 = __commonJS((exports, module) => {
289030
289030
  var require_lib39 = __commonJS((exports, module) => {
289031
289031
  var fs = __require("fs");
289032
289032
  var { promisify } = __require("util");
289033
- var { readFileSync } = fs;
289033
+ var { readFileSync: readFileSync2 } = fs;
289034
289034
  var readFile = promisify(fs.readFile);
289035
289035
  var extractPath = (path, cmdshimContents) => {
289036
289036
  if (/[.]cmd$/.test(path)) {
@@ -289082,7 +289082,7 @@ var require_lib39 = __commonJS((exports, module) => {
289082
289082
  });
289083
289083
  };
289084
289084
  var readCmdShimSync = (path) => {
289085
- const contents = readFileSync(path);
289085
+ const contents = readFileSync2(path);
289086
289086
  const destination = extractPath(path, contents.toString());
289087
289087
  if (!destination) {
289088
289088
  throw notaShim(path);
@@ -308272,7 +308272,7 @@ var require_getImage = __commonJS((exports) => {
308272
308272
  exports.getImageOutput = exports.getImage = undefined;
308273
308273
  var pulumi = require_pulumi();
308274
308274
  var utilities = require_utilities();
308275
- function getImage(args, opts) {
308275
+ function getImage2(args, opts) {
308276
308276
  args = args || {};
308277
308277
  opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts || {});
308278
308278
  return pulumi.runtime.invoke("digitalocean:index/getImage:getImage", {
@@ -308282,7 +308282,7 @@ var require_getImage = __commonJS((exports) => {
308282
308282
  source: args.source
308283
308283
  }, opts);
308284
308284
  }
308285
- exports.getImage = getImage;
308285
+ exports.getImage = getImage2;
308286
308286
  function getImageOutput(args, opts) {
308287
308287
  args = args || {};
308288
308288
  opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts || {});
@@ -337003,23 +337003,69 @@ var defaultHealthCheck = {
337003
337003
  httpPath: "/health"
337004
337004
  };
337005
337005
  // lib/helpers/discover-services.ts
337006
- import { readdirSync } from "node:fs";
337006
+ import { readdirSync, readFileSync } from "node:fs";
337007
337007
  import { isAbsolute, join, resolve } from "node:path";
337008
- function getServicePort(config) {
337009
- if (config.httpPort)
337010
- return config.httpPort;
337011
- const internalPorts = config.internalPorts;
337012
- if (internalPorts?.[0])
337013
- return internalPorts[0];
337014
- return 8080;
337008
+
337009
+ // lib/helpers/image.ts
337010
+ var scopeImageTagsRaw = process.env.SCOPE_IMAGE_TAGS ?? "";
337011
+ var scopeImageTags = (() => {
337012
+ if (!scopeImageTagsRaw.trim()) {
337013
+ return {};
337014
+ }
337015
+ try {
337016
+ const parsed = JSON.parse(scopeImageTagsRaw);
337017
+ const tags = {};
337018
+ for (const [key, value] of Object.entries(parsed ?? {})) {
337019
+ if (typeof value === "string" && value.trim().length > 0) {
337020
+ tags[key] = value.trim();
337021
+ }
337022
+ }
337023
+ return tags;
337024
+ } catch (error) {
337025
+ console.warn("Unable to parse scope image tags from environment:", error);
337026
+ return {};
337027
+ }
337028
+ })();
337029
+ var resolveImageTag = (scopeName) => {
337030
+ const tag = scopeImageTags[scopeName];
337031
+ if (!tag) {
337032
+ return "latest";
337033
+ }
337034
+ return tag;
337035
+ };
337036
+ var ghcrImage = (registry, serviceName) => {
337037
+ const tag = resolveImageTag(serviceName);
337038
+ return `ghcr.io/${registry}/${serviceName}:${tag}`;
337039
+ };
337040
+ var getImage = (registry, repository, registryCredentials) => {
337041
+ const scopeName = repository.split("/").pop();
337042
+ if (!scopeName) {
337043
+ throw new Error(`Invalid repository name: ${repository}`);
337044
+ }
337045
+ return {
337046
+ registryType: "GHCR",
337047
+ registry,
337048
+ repository,
337049
+ registryCredentials,
337050
+ tag: resolveImageTag(scopeName)
337051
+ };
337052
+ };
337053
+
337054
+ // lib/helpers/discover-services.ts
337055
+ function getRegistryFromPackageJson(workspaceRoot) {
337056
+ const pkgPath = join(workspaceRoot, "package.json");
337057
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
337058
+ if (!pkg.pf?.registry) {
337059
+ throw new Error(`Missing "pf.registry" in ${pkgPath}. Add it to your root package.json, e.g.: "pf": { "registry": "orgname/reponame" }`);
337060
+ }
337061
+ return pkg.pf.registry;
337015
337062
  }
337016
337063
  function validateNoDuplicatePorts(configs) {
337017
337064
  const portMap = new Map;
337018
337065
  for (const config of configs) {
337019
- const port = getServicePort(config);
337020
- const existing = portMap.get(port) || [];
337066
+ const existing = portMap.get(config.containerPort) || [];
337021
337067
  existing.push(config.name);
337022
- portMap.set(port, existing);
337068
+ portMap.set(config.containerPort, existing);
337023
337069
  }
337024
337070
  const conflicts = [...portMap.entries()].filter(([, services]) => services.length > 1).map(([port, services]) => `Port ${port}: ${services.join(", ")}`);
337025
337071
  if (conflicts.length > 0) {
@@ -337028,13 +337074,22 @@ ${conflicts.join(`
337028
337074
  `)}`);
337029
337075
  }
337030
337076
  }
337031
- function discoverServices(servicesDir) {
337077
+ function discoverServiceConfigs(servicesDir = "infra/services") {
337032
337078
  const resolvedDir = isAbsolute(servicesDir) ? servicesDir : resolve(process.cwd(), servicesDir);
337079
+ const workspaceRoot = resolve(resolvedDir, "..", "..");
337080
+ const registry = getRegistryFromPackageJson(workspaceRoot);
337033
337081
  const files = readdirSync(resolvedDir).filter((file) => file.endsWith(".ts") && file !== "index.ts");
337034
337082
  const configs = files.map((file) => {
337035
337083
  const filePath = join(resolvedDir, file);
337036
337084
  const module = __require(filePath);
337037
- return module.default ?? module;
337085
+ const config = module.default ?? module;
337086
+ if (!config.image) {
337087
+ return {
337088
+ ...config,
337089
+ image: ghcrImage(registry, config.name)
337090
+ };
337091
+ }
337092
+ return config;
337038
337093
  });
337039
337094
  validateNoDuplicatePorts(configs);
337040
337095
  return configs;
@@ -337262,50 +337317,6 @@ function buildDropletServices(options) {
337262
337317
  function filterByPlatform(configs, platform) {
337263
337318
  return configs.filter((c) => (c.platform || "app-platform") === platform);
337264
337319
  }
337265
- // lib/helpers/image.ts
337266
- var scopeImageTagsRaw = process.env.SCOPE_IMAGE_TAGS ?? "";
337267
- var scopeImageTags = (() => {
337268
- if (!scopeImageTagsRaw.trim()) {
337269
- return {};
337270
- }
337271
- try {
337272
- const parsed = JSON.parse(scopeImageTagsRaw);
337273
- const tags = {};
337274
- for (const [key, value] of Object.entries(parsed ?? {})) {
337275
- if (typeof value === "string" && value.trim().length > 0) {
337276
- tags[key] = value.trim();
337277
- }
337278
- }
337279
- return tags;
337280
- } catch (error) {
337281
- console.warn("Unable to parse scope image tags from environment:", error);
337282
- return {};
337283
- }
337284
- })();
337285
- var resolveImageTag = (scopeName) => {
337286
- const tag = scopeImageTags[scopeName];
337287
- if (!tag) {
337288
- return "latest";
337289
- }
337290
- return tag;
337291
- };
337292
- var ghcrImage = (registry, serviceName) => {
337293
- const tag = resolveImageTag(serviceName);
337294
- return `ghcr.io/${registry}/${serviceName}:${tag}`;
337295
- };
337296
- var getImage = (registry, repository, registryCredentials) => {
337297
- const scopeName = repository.split("/").pop();
337298
- if (!scopeName) {
337299
- throw new Error(`Invalid repository name: ${repository}`);
337300
- }
337301
- return {
337302
- registryType: "GHCR",
337303
- registry,
337304
- repository,
337305
- registryCredentials,
337306
- tag: resolveImageTag(scopeName)
337307
- };
337308
- };
337309
337320
  // lib/helpers/service-builder.ts
337310
337321
  function buildServices(options) {
337311
337322
  const { serviceConfigs, registryCredentials, logtailToken, registry } = options;
@@ -337350,7 +337361,7 @@ function buildIngressRules(serviceConfigs) {
337350
337361
  }
337351
337362
  // lib/helpers/service-runtime.ts
337352
337363
  var toEnvKey = (name) => name.toUpperCase().replace(/-/g, "_");
337353
- function getServicePort2(serviceName, defaultPort = 8080) {
337364
+ function getServicePort(serviceName, defaultPort = 8080) {
337354
337365
  const envKey = `${toEnvKey(serviceName)}_PORT`;
337355
337366
  const envValue = process.env[envKey];
337356
337367
  if (envValue) {
@@ -337366,7 +337377,7 @@ function getServiceUrl(serviceName) {
337366
337377
  return process.env[envKey];
337367
337378
  }
337368
337379
  // lib/helpers/service-urls.ts
337369
- function getServicePort3(config) {
337380
+ function getServicePort2(config) {
337370
337381
  if (config.httpPort)
337371
337382
  return config.httpPort;
337372
337383
  const internalPorts = config.internalPorts;
@@ -337376,7 +337387,7 @@ function getServicePort3(config) {
337376
337387
  }
337377
337388
  function buildInternalUrls(serviceConfigs) {
337378
337389
  return Object.fromEntries(serviceConfigs.map((config) => {
337379
- const url = config.internalUrl ?? `http://${config.name}:${getServicePort3(config)}`;
337390
+ const url = config.internalUrl ?? `http://${config.name}:${getServicePort2(config)}`;
337380
337391
  return [config.name, url];
337381
337392
  }));
337382
337393
  }
@@ -337388,7 +337399,7 @@ function buildExternalUrls(serviceConfigs, baseUrl) {
337388
337399
  }
337389
337400
  function buildLocalUrls(serviceConfigs) {
337390
337401
  return Object.fromEntries(serviceConfigs.map((config) => {
337391
- const port = getServicePort3(config);
337402
+ const port = getServicePort2(config);
337392
337403
  return [config.name, `http://localhost:${port}`];
337393
337404
  }));
337394
337405
  }
@@ -337396,14 +337407,14 @@ function buildServiceUrlEnvs(serviceConfigs) {
337396
337407
  return serviceConfigs.map((config) => ({
337397
337408
  key: `${config.name.toUpperCase().replace(/-/g, "_")}_URL`,
337398
337409
  scope: "RUN_TIME",
337399
- value: config.internalUrl ?? `http://${config.name}:${getServicePort3(config)}`
337410
+ value: config.internalUrl ?? `http://${config.name}:${getServicePort2(config)}`
337400
337411
  }));
337401
337412
  }
337402
337413
  function buildServicePortEnvs(serviceConfigs) {
337403
337414
  return serviceConfigs.map((config) => ({
337404
337415
  key: `${config.name.toUpperCase().replace(/-/g, "_")}_PORT`,
337405
337416
  scope: "RUN_TIME",
337406
- value: String(getServicePort3(config))
337417
+ value: String(getServicePort2(config))
337407
337418
  }));
337408
337419
  }
337409
337420
  // lib/runtimes/doks/cert-manager.ts
@@ -337769,6 +337780,9 @@ function getImagePullPolicy(image2, explicit) {
337769
337780
  return image2.endsWith(":latest") ? "Always" : "IfNotPresent";
337770
337781
  }
337771
337782
  function deployK8sService(provider, namespace, config2) {
337783
+ if (!config2.image) {
337784
+ throw new Error(`Missing "image" for service "${config2.name}". Either specify image explicitly or use discoverServiceConfigs() which auto-generates it from pf.registry.`);
337785
+ }
337772
337786
  const labels = buildLabels(config2.name, config2.labels);
337773
337787
  const replicas = config2.replicas ?? DEFAULTS.replicas;
337774
337788
  const imagePullPolicy = getImagePullPolicy(config2.image, config2.imagePullPolicy);
@@ -338060,12 +338074,12 @@ function buildInternalUrl(serviceName, namespace, port) {
338060
338074
  export {
338061
338075
  ghcrImage,
338062
338076
  getServiceUrl,
338063
- getServicePort2 as getServicePort,
338077
+ getServicePort,
338064
338078
  getImage,
338065
338079
  filterByPlatform,
338066
338080
  ensureDot,
338067
338081
  dockerHubImage,
338068
- discoverServices,
338082
+ discoverServiceConfigs,
338069
338083
  deployNginxIngress,
338070
338084
  deployNats,
338071
338085
  deployK8sServices,
@@ -197,8 +197,11 @@ export interface K8sVolumeMount {
197
197
  export interface K8sServiceConfig {
198
198
  /** Unique name of the service (used for deployment, service, and labels) */
199
199
  name: string;
200
- /** Container image (e.g., 'ghcr.io/orderboss/platform/storefront:latest') */
201
- image: string;
200
+ /**
201
+ * Container image (e.g., 'ghcr.io/orderboss/platform/storefront:latest').
202
+ * If not specified, auto-generated from pf.registry config + service name.
203
+ */
204
+ image?: string;
202
205
  /** Port the container listens on */
203
206
  containerPort: number;
204
207
  /** Number of replicas (defaults to 1) */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/infrastructure",
3
- "version": "0.2.22",
3
+ "version": "0.2.23",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "publishConfig": {