@geekmidas/cli 10.0.0-alpha.49 → 10.0.0-alpha.50
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/{HostingerProvider-Bh6K7QYz.mjs → HostingerProvider-GF6NAZAK.mjs} +2 -2
- package/dist/{HostingerProvider-Bh6K7QYz.mjs.map → HostingerProvider-GF6NAZAK.mjs.map} +1 -1
- package/dist/index.cjs +242 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +239 -49
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-DhxTIA0z.mjs → migrate-B00XpVaO.mjs} +2 -2
- package/dist/{migrate-DhxTIA0z.mjs.map → migrate-B00XpVaO.mjs.map} +1 -1
- package/dist/{migrate-BgEY9pdP.cjs → migrate-CNlew9Ek.cjs} +2 -2
- package/dist/{migrate-BgEY9pdP.cjs.map → migrate-CNlew9Ek.cjs.map} +1 -1
- package/dist/{workspace-cb6U29EL.mjs → workspace-BsbxbqV-.mjs} +51 -7
- package/dist/workspace-BsbxbqV-.mjs.map +1 -0
- package/dist/{workspace-vafrzZTC.cjs → workspace-j6ZxvJqq.cjs} +50 -6
- package/dist/workspace-j6ZxvJqq.cjs.map +1 -0
- package/package.json +14 -14
- package/dist/workspace-cb6U29EL.mjs.map +0 -1
- package/dist/workspace-vafrzZTC.cjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as getHostingerToken } from "./index.mjs";
|
|
2
2
|
//#region src/deploy/dns/hostinger-api.ts
|
|
3
3
|
/**
|
|
4
4
|
* Hostinger DNS API client
|
|
@@ -240,4 +240,4 @@ var HostingerProvider = class {
|
|
|
240
240
|
//#endregion
|
|
241
241
|
export { HostingerProvider };
|
|
242
242
|
|
|
243
|
-
//# sourceMappingURL=HostingerProvider-
|
|
243
|
+
//# sourceMappingURL=HostingerProvider-GF6NAZAK.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HostingerProvider-Bh6K7QYz.mjs","names":[],"sources":["../src/deploy/dns/hostinger-api.ts","../src/deploy/dns/HostingerProvider.ts"],"sourcesContent":["/**\n * Hostinger DNS API client\n *\n * API Documentation: https://developers.hostinger.com/\n * Authentication: Bearer token from hpanel.hostinger.com/profile/api\n */\n\nconst HOSTINGER_API_BASE = 'https://developers.hostinger.com';\n\n/**\n * DNS record types supported by Hostinger\n */\nexport type DnsRecordType =\n\t| 'A'\n\t| 'AAAA'\n\t| 'CNAME'\n\t| 'MX'\n\t| 'TXT'\n\t| 'NS'\n\t| 'SRV'\n\t| 'CAA';\n\n/**\n * A single DNS record\n */\nexport interface DnsRecord {\n\t/** Subdomain name (e.g., 'api.joemoer' for api.joemoer.traflabs.io) */\n\tname: string;\n\t/** Record type */\n\ttype: DnsRecordType;\n\t/** TTL in seconds */\n\tttl: number;\n\t/** Record values */\n\trecords: Array<{ content: string }>;\n}\n\n/**\n * Filter for deleting specific records\n */\nexport interface DnsRecordFilter {\n\tname: string;\n\ttype: DnsRecordType;\n}\n\n/**\n * API error response\n */\nexport interface HostingerErrorResponse {\n\tmessage?: string;\n\terrors?: Record<string, string[]>;\n}\n\n/**\n * Hostinger API error\n */\nexport class HostingerApiError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t\tpublic statusText: string,\n\t\tpublic errors?: Record<string, string[]>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'HostingerApiError';\n\t}\n}\n\n/**\n * Hostinger DNS API client\n *\n * @example\n * ```ts\n * const api = new HostingerApi(token);\n *\n * // Get all records for a domain\n * const records = await api.getRecords('traflabs.io');\n *\n * // Create/update records\n * await api.upsertRecords('traflabs.io', [\n * { name: 'api.joemoer', type: 'A', ttl: 300, records: ['1.2.3.4'] }\n * ]);\n * ```\n */\nexport class HostingerApi {\n\tprivate token: string;\n\n\tconstructor(token: string) {\n\t\tthis.token = token;\n\t}\n\n\t/**\n\t * Make a request to the Hostinger API\n\t */\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE',\n\t\tendpoint: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst url = `${HOSTINGER_API_BASE}${endpoint}`;\n\n\t\tconst response = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\tAuthorization: `Bearer ${this.token}`,\n\t\t\t},\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tlet errorMessage = `Hostinger API error: ${response.status} ${response.statusText}`;\n\t\t\tlet errors: Record<string, string[]> | undefined;\n\n\t\t\ttry {\n\t\t\t\tconst errorBody = (await response.json()) as HostingerErrorResponse;\n\t\t\t\tif (errorBody.message) {\n\t\t\t\t\terrorMessage = `Hostinger API error: ${errorBody.message}`;\n\t\t\t\t}\n\t\t\t\terrors = errorBody.errors;\n\t\t\t} catch {\n\t\t\t\t// Ignore JSON parse errors\n\t\t\t}\n\n\t\t\tthrow new HostingerApiError(\n\t\t\t\terrorMessage,\n\t\t\t\tresponse.status,\n\t\t\t\tresponse.statusText,\n\t\t\t\terrors,\n\t\t\t);\n\t\t}\n\n\t\t// Handle empty responses\n\t\tconst text = await response.text();\n\t\tif (!text || text.trim() === '') {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn JSON.parse(text) as T;\n\t}\n\n\t/**\n\t * Get all DNS records for a domain\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t */\n\tasync getRecords(domain: string): Promise<DnsRecord[]> {\n\t\t// The zone comes back as a **bare array**, not `{ data: [...] }`.\n\t\t//\n\t\t// This read `response.data || []`, so every call returned zero records\n\t\t// against the real API while every mocked test passed. Silently: no error,\n\t\t// no warning, just an empty zone. `upsertRecords` uses this to decide what\n\t\t// already exists, so everything looked new and every write was reported as\n\t\t// a creation — harmless only because the PUT merges rather than replaces.\n\t\t//\n\t\t// Both shapes are accepted because one of them is a guess: the wrapper was\n\t\t// written against documentation and the array is what the API actually\n\t\t// sends, so tolerating an envelope costs a line and survives it appearing.\n\t\tconst response = await this.request<DnsRecord[] | { data?: DnsRecord[] }>(\n\t\t\t'GET',\n\t\t\t`/api/dns/v1/zones/${domain}`,\n\t\t);\n\n\t\tif (Array.isArray(response)) return response;\n\n\t\treturn response?.data ?? [];\n\t}\n\n\t/**\n\t * Create or update DNS records\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param records - Records to create/update\n\t * @param overwrite - If true, replaces all existing records. If false, merges with existing.\n\t */\n\tasync upsertRecords(\n\t\tdomain: string,\n\t\trecords: DnsRecord[],\n\t\toverwrite = false,\n\t): Promise<void> {\n\t\tawait this.request('PUT', `/api/dns/v1/zones/${domain}`, {\n\t\t\toverwrite,\n\t\t\tzone: records,\n\t\t});\n\t}\n\n\t/**\n\t * Validate DNS records before applying\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param records - Records to validate\n\t * @returns true if valid, throws if invalid\n\t */\n\tasync validateRecords(\n\t\tdomain: string,\n\t\trecords: DnsRecord[],\n\t): Promise<boolean> {\n\t\tawait this.request('POST', `/api/dns/v1/zones/${domain}/validate`, {\n\t\t\toverwrite: false,\n\t\t\tzone: records,\n\t\t});\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delete specific DNS records\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param filters - Filters to match records for deletion\n\t */\n\tasync deleteRecords(\n\t\tdomain: string,\n\t\tfilters: DnsRecordFilter[],\n\t): Promise<void> {\n\t\tawait this.request('DELETE', `/api/dns/v1/zones/${domain}`, {\n\t\t\tfilters,\n\t\t});\n\t}\n\n\t/**\n\t * Check if a specific record exists\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param name - Subdomain name (e.g., 'api.joemoer')\n\t * @param type - Record type (e.g., 'A')\n\t */\n\tasync recordExists(\n\t\tdomain: string,\n\t\tname: string,\n\t\ttype: DnsRecordType = 'A',\n\t): Promise<boolean> {\n\t\tconst records = await this.getRecords(domain);\n\t\treturn records.some((r) => r.name === name && r.type === type);\n\t}\n\n\t/**\n\t * Create a single A record if it doesn't exist\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param subdomain - Subdomain name (e.g., 'api.joemoer')\n\t * @param ip - IP address to point to\n\t * @param ttl - TTL in seconds (default: 300)\n\t * @returns true if created, false if already exists\n\t */\n\tasync createARecordIfNotExists(\n\t\tdomain: string,\n\t\tsubdomain: string,\n\t\tip: string,\n\t\tttl = 300,\n\t): Promise<boolean> {\n\t\tconst exists = await this.recordExists(domain, subdomain, 'A');\n\t\tif (exists) {\n\t\t\treturn false;\n\t\t}\n\n\t\tawait this.upsertRecords(domain, [\n\t\t\t{\n\t\t\t\tname: subdomain,\n\t\t\t\ttype: 'A',\n\t\t\t\tttl,\n\t\t\t\trecords: [{ content: ip }],\n\t\t\t},\n\t\t]);\n\n\t\treturn true;\n\t}\n}\n","/**\n * Hostinger DNS Provider\n *\n * Implements DnsProvider interface using the Hostinger DNS API.\n */\n\nimport { getHostingerToken } from '../../auth/credentials';\nimport type {\n\tDeleteDnsRecord,\n\tDeleteResult,\n\tDnsProvider,\n\tDnsRecord,\n\tUpsertDnsRecord,\n\tUpsertResult,\n} from './DnsProvider';\nimport { HostingerApi } from './hostinger-api';\n\n/**\n * Hostinger DNS provider implementation.\n */\nexport class HostingerProvider implements DnsProvider {\n\treadonly name = 'hostinger';\n\tprivate api: HostingerApi | null = null;\n\n\t/**\n\t * Get or create the Hostinger API client.\n\t */\n\tprivate async getApi(): Promise<HostingerApi> {\n\t\tif (this.api) {\n\t\t\treturn this.api;\n\t\t}\n\n\t\tconst token = await getHostingerToken();\n\t\tif (!token) {\n\t\t\tthrow new Error(\n\t\t\t\t'Hostinger API token not configured. Run `gkm login --service=hostinger` or get your token from https://hpanel.hostinger.com/profile/api',\n\t\t\t);\n\t\t}\n\n\t\tthis.api = new HostingerApi(token);\n\t\treturn this.api;\n\t}\n\n\tasync getRecords(domain: string): Promise<DnsRecord[]> {\n\t\tconst api = await this.getApi();\n\t\tconst records = await api.getRecords(domain);\n\n\t\treturn records.map((r) => ({\n\t\t\tname: r.name,\n\t\t\ttype: r.type,\n\t\t\tttl: r.ttl,\n\t\t\tvalues: r.records.map((rec) => rec.content),\n\t\t}));\n\t}\n\n\tasync upsertRecords(\n\t\tdomain: string,\n\t\trecords: UpsertDnsRecord[],\n\t): Promise<UpsertResult[]> {\n\t\tconst api = await this.getApi();\n\t\tconst results: UpsertResult[] = [];\n\n\t\t// Get existing records to check what already exists\n\t\tconst existingRecords = await api.getRecords(domain);\n\n\t\tfor (const record of records) {\n\t\t\tconst existing = existingRecords.find(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t);\n\n\t\t\tconst existingValue = existing?.records?.[0]?.content;\n\n\t\t\tif (existing && existingValue === record.value) {\n\t\t\t\t// Record exists with same value - unchanged\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tcreated: false,\n\t\t\t\t\tunchanged: true,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Create or update the record\n\t\t\tawait api.upsertRecords(domain, [\n\t\t\t\t{\n\t\t\t\t\tname: record.name,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tttl: record.ttl,\n\t\t\t\t\trecords: [{ content: record.value }],\n\t\t\t\t},\n\t\t\t]);\n\n\t\t\tresults.push({\n\t\t\t\trecord,\n\t\t\t\tcreated: !existing,\n\t\t\t\tunchanged: false,\n\t\t\t});\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tasync deleteRecords(\n\t\tdomain: string,\n\t\trecords: DeleteDnsRecord[],\n\t): Promise<DeleteResult[]> {\n\t\tconst api = await this.getApi();\n\t\tconst results: DeleteResult[] = [];\n\n\t\t// Get existing records to check what exists\n\t\tconst existingRecords = await api.getRecords(domain);\n\n\t\t// Filter to only records that exist\n\t\tconst recordsToDelete = records.filter((record) =>\n\t\t\texistingRecords.some(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t),\n\t\t);\n\n\t\t// Delete existing records\n\t\tif (recordsToDelete.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait api.deleteRecords(\n\t\t\t\t\tdomain,\n\t\t\t\t\trecordsToDelete.map((r) => ({\n\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\ttype: r.type as\n\t\t\t\t\t\t\t| 'A'\n\t\t\t\t\t\t\t| 'AAAA'\n\t\t\t\t\t\t\t| 'CNAME'\n\t\t\t\t\t\t\t| 'MX'\n\t\t\t\t\t\t\t| 'TXT'\n\t\t\t\t\t\t\t| 'SRV'\n\t\t\t\t\t\t\t| 'CAA',\n\t\t\t\t\t})),\n\t\t\t\t);\n\n\t\t\t\tfor (const record of recordsToDelete) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tdeleted: true,\n\t\t\t\t\t\tnotFound: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// If batch delete fails, report error for all records\n\t\t\t\tfor (const record of recordsToDelete) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\tnotFound: false,\n\t\t\t\t\t\terror: String(error),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Mark non-existent records as not found\n\t\tfor (const record of records) {\n\t\t\tconst existing = existingRecords.find(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t);\n\t\t\tif (!existing) {\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tdeleted: false,\n\t\t\t\t\tnotFound: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HostingerProvider-GF6NAZAK.mjs","names":[],"sources":["../src/deploy/dns/hostinger-api.ts","../src/deploy/dns/HostingerProvider.ts"],"sourcesContent":["/**\n * Hostinger DNS API client\n *\n * API Documentation: https://developers.hostinger.com/\n * Authentication: Bearer token from hpanel.hostinger.com/profile/api\n */\n\nconst HOSTINGER_API_BASE = 'https://developers.hostinger.com';\n\n/**\n * DNS record types supported by Hostinger\n */\nexport type DnsRecordType =\n\t| 'A'\n\t| 'AAAA'\n\t| 'CNAME'\n\t| 'MX'\n\t| 'TXT'\n\t| 'NS'\n\t| 'SRV'\n\t| 'CAA';\n\n/**\n * A single DNS record\n */\nexport interface DnsRecord {\n\t/** Subdomain name (e.g., 'api.joemoer' for api.joemoer.traflabs.io) */\n\tname: string;\n\t/** Record type */\n\ttype: DnsRecordType;\n\t/** TTL in seconds */\n\tttl: number;\n\t/** Record values */\n\trecords: Array<{ content: string }>;\n}\n\n/**\n * Filter for deleting specific records\n */\nexport interface DnsRecordFilter {\n\tname: string;\n\ttype: DnsRecordType;\n}\n\n/**\n * API error response\n */\nexport interface HostingerErrorResponse {\n\tmessage?: string;\n\terrors?: Record<string, string[]>;\n}\n\n/**\n * Hostinger API error\n */\nexport class HostingerApiError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t\tpublic statusText: string,\n\t\tpublic errors?: Record<string, string[]>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'HostingerApiError';\n\t}\n}\n\n/**\n * Hostinger DNS API client\n *\n * @example\n * ```ts\n * const api = new HostingerApi(token);\n *\n * // Get all records for a domain\n * const records = await api.getRecords('traflabs.io');\n *\n * // Create/update records\n * await api.upsertRecords('traflabs.io', [\n * { name: 'api.joemoer', type: 'A', ttl: 300, records: ['1.2.3.4'] }\n * ]);\n * ```\n */\nexport class HostingerApi {\n\tprivate token: string;\n\n\tconstructor(token: string) {\n\t\tthis.token = token;\n\t}\n\n\t/**\n\t * Make a request to the Hostinger API\n\t */\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE',\n\t\tendpoint: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst url = `${HOSTINGER_API_BASE}${endpoint}`;\n\n\t\tconst response = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\tAuthorization: `Bearer ${this.token}`,\n\t\t\t},\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tlet errorMessage = `Hostinger API error: ${response.status} ${response.statusText}`;\n\t\t\tlet errors: Record<string, string[]> | undefined;\n\n\t\t\ttry {\n\t\t\t\tconst errorBody = (await response.json()) as HostingerErrorResponse;\n\t\t\t\tif (errorBody.message) {\n\t\t\t\t\terrorMessage = `Hostinger API error: ${errorBody.message}`;\n\t\t\t\t}\n\t\t\t\terrors = errorBody.errors;\n\t\t\t} catch {\n\t\t\t\t// Ignore JSON parse errors\n\t\t\t}\n\n\t\t\tthrow new HostingerApiError(\n\t\t\t\terrorMessage,\n\t\t\t\tresponse.status,\n\t\t\t\tresponse.statusText,\n\t\t\t\terrors,\n\t\t\t);\n\t\t}\n\n\t\t// Handle empty responses\n\t\tconst text = await response.text();\n\t\tif (!text || text.trim() === '') {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn JSON.parse(text) as T;\n\t}\n\n\t/**\n\t * Get all DNS records for a domain\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t */\n\tasync getRecords(domain: string): Promise<DnsRecord[]> {\n\t\t// The zone comes back as a **bare array**, not `{ data: [...] }`.\n\t\t//\n\t\t// This read `response.data || []`, so every call returned zero records\n\t\t// against the real API while every mocked test passed. Silently: no error,\n\t\t// no warning, just an empty zone. `upsertRecords` uses this to decide what\n\t\t// already exists, so everything looked new and every write was reported as\n\t\t// a creation — harmless only because the PUT merges rather than replaces.\n\t\t//\n\t\t// Both shapes are accepted because one of them is a guess: the wrapper was\n\t\t// written against documentation and the array is what the API actually\n\t\t// sends, so tolerating an envelope costs a line and survives it appearing.\n\t\tconst response = await this.request<DnsRecord[] | { data?: DnsRecord[] }>(\n\t\t\t'GET',\n\t\t\t`/api/dns/v1/zones/${domain}`,\n\t\t);\n\n\t\tif (Array.isArray(response)) return response;\n\n\t\treturn response?.data ?? [];\n\t}\n\n\t/**\n\t * Create or update DNS records\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param records - Records to create/update\n\t * @param overwrite - If true, replaces all existing records. If false, merges with existing.\n\t */\n\tasync upsertRecords(\n\t\tdomain: string,\n\t\trecords: DnsRecord[],\n\t\toverwrite = false,\n\t): Promise<void> {\n\t\tawait this.request('PUT', `/api/dns/v1/zones/${domain}`, {\n\t\t\toverwrite,\n\t\t\tzone: records,\n\t\t});\n\t}\n\n\t/**\n\t * Validate DNS records before applying\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param records - Records to validate\n\t * @returns true if valid, throws if invalid\n\t */\n\tasync validateRecords(\n\t\tdomain: string,\n\t\trecords: DnsRecord[],\n\t): Promise<boolean> {\n\t\tawait this.request('POST', `/api/dns/v1/zones/${domain}/validate`, {\n\t\t\toverwrite: false,\n\t\t\tzone: records,\n\t\t});\n\t\treturn true;\n\t}\n\n\t/**\n\t * Delete specific DNS records\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param filters - Filters to match records for deletion\n\t */\n\tasync deleteRecords(\n\t\tdomain: string,\n\t\tfilters: DnsRecordFilter[],\n\t): Promise<void> {\n\t\tawait this.request('DELETE', `/api/dns/v1/zones/${domain}`, {\n\t\t\tfilters,\n\t\t});\n\t}\n\n\t/**\n\t * Check if a specific record exists\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param name - Subdomain name (e.g., 'api.joemoer')\n\t * @param type - Record type (e.g., 'A')\n\t */\n\tasync recordExists(\n\t\tdomain: string,\n\t\tname: string,\n\t\ttype: DnsRecordType = 'A',\n\t): Promise<boolean> {\n\t\tconst records = await this.getRecords(domain);\n\t\treturn records.some((r) => r.name === name && r.type === type);\n\t}\n\n\t/**\n\t * Create a single A record if it doesn't exist\n\t *\n\t * @param domain - Root domain (e.g., 'traflabs.io')\n\t * @param subdomain - Subdomain name (e.g., 'api.joemoer')\n\t * @param ip - IP address to point to\n\t * @param ttl - TTL in seconds (default: 300)\n\t * @returns true if created, false if already exists\n\t */\n\tasync createARecordIfNotExists(\n\t\tdomain: string,\n\t\tsubdomain: string,\n\t\tip: string,\n\t\tttl = 300,\n\t): Promise<boolean> {\n\t\tconst exists = await this.recordExists(domain, subdomain, 'A');\n\t\tif (exists) {\n\t\t\treturn false;\n\t\t}\n\n\t\tawait this.upsertRecords(domain, [\n\t\t\t{\n\t\t\t\tname: subdomain,\n\t\t\t\ttype: 'A',\n\t\t\t\tttl,\n\t\t\t\trecords: [{ content: ip }],\n\t\t\t},\n\t\t]);\n\n\t\treturn true;\n\t}\n}\n","/**\n * Hostinger DNS Provider\n *\n * Implements DnsProvider interface using the Hostinger DNS API.\n */\n\nimport { getHostingerToken } from '../../auth/credentials';\nimport type {\n\tDeleteDnsRecord,\n\tDeleteResult,\n\tDnsProvider,\n\tDnsRecord,\n\tUpsertDnsRecord,\n\tUpsertResult,\n} from './DnsProvider';\nimport { HostingerApi } from './hostinger-api';\n\n/**\n * Hostinger DNS provider implementation.\n */\nexport class HostingerProvider implements DnsProvider {\n\treadonly name = 'hostinger';\n\tprivate api: HostingerApi | null = null;\n\n\t/**\n\t * Get or create the Hostinger API client.\n\t */\n\tprivate async getApi(): Promise<HostingerApi> {\n\t\tif (this.api) {\n\t\t\treturn this.api;\n\t\t}\n\n\t\tconst token = await getHostingerToken();\n\t\tif (!token) {\n\t\t\tthrow new Error(\n\t\t\t\t'Hostinger API token not configured. Run `gkm login --service=hostinger` or get your token from https://hpanel.hostinger.com/profile/api',\n\t\t\t);\n\t\t}\n\n\t\tthis.api = new HostingerApi(token);\n\t\treturn this.api;\n\t}\n\n\tasync getRecords(domain: string): Promise<DnsRecord[]> {\n\t\tconst api = await this.getApi();\n\t\tconst records = await api.getRecords(domain);\n\n\t\treturn records.map((r) => ({\n\t\t\tname: r.name,\n\t\t\ttype: r.type,\n\t\t\tttl: r.ttl,\n\t\t\tvalues: r.records.map((rec) => rec.content),\n\t\t}));\n\t}\n\n\tasync upsertRecords(\n\t\tdomain: string,\n\t\trecords: UpsertDnsRecord[],\n\t): Promise<UpsertResult[]> {\n\t\tconst api = await this.getApi();\n\t\tconst results: UpsertResult[] = [];\n\n\t\t// Get existing records to check what already exists\n\t\tconst existingRecords = await api.getRecords(domain);\n\n\t\tfor (const record of records) {\n\t\t\tconst existing = existingRecords.find(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t);\n\n\t\t\tconst existingValue = existing?.records?.[0]?.content;\n\n\t\t\tif (existing && existingValue === record.value) {\n\t\t\t\t// Record exists with same value - unchanged\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tcreated: false,\n\t\t\t\t\tunchanged: true,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Create or update the record\n\t\t\tawait api.upsertRecords(domain, [\n\t\t\t\t{\n\t\t\t\t\tname: record.name,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\tttl: record.ttl,\n\t\t\t\t\trecords: [{ content: record.value }],\n\t\t\t\t},\n\t\t\t]);\n\n\t\t\tresults.push({\n\t\t\t\trecord,\n\t\t\t\tcreated: !existing,\n\t\t\t\tunchanged: false,\n\t\t\t});\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tasync deleteRecords(\n\t\tdomain: string,\n\t\trecords: DeleteDnsRecord[],\n\t): Promise<DeleteResult[]> {\n\t\tconst api = await this.getApi();\n\t\tconst results: DeleteResult[] = [];\n\n\t\t// Get existing records to check what exists\n\t\tconst existingRecords = await api.getRecords(domain);\n\n\t\t// Filter to only records that exist\n\t\tconst recordsToDelete = records.filter((record) =>\n\t\t\texistingRecords.some(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t),\n\t\t);\n\n\t\t// Delete existing records\n\t\tif (recordsToDelete.length > 0) {\n\t\t\ttry {\n\t\t\t\tawait api.deleteRecords(\n\t\t\t\t\tdomain,\n\t\t\t\t\trecordsToDelete.map((r) => ({\n\t\t\t\t\t\tname: r.name,\n\t\t\t\t\t\ttype: r.type as\n\t\t\t\t\t\t\t| 'A'\n\t\t\t\t\t\t\t| 'AAAA'\n\t\t\t\t\t\t\t| 'CNAME'\n\t\t\t\t\t\t\t| 'MX'\n\t\t\t\t\t\t\t| 'TXT'\n\t\t\t\t\t\t\t| 'SRV'\n\t\t\t\t\t\t\t| 'CAA',\n\t\t\t\t\t})),\n\t\t\t\t);\n\n\t\t\t\tfor (const record of recordsToDelete) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tdeleted: true,\n\t\t\t\t\t\tnotFound: false,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// If batch delete fails, report error for all records\n\t\t\t\tfor (const record of recordsToDelete) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\trecord,\n\t\t\t\t\t\tdeleted: false,\n\t\t\t\t\t\tnotFound: false,\n\t\t\t\t\t\terror: String(error),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Mark non-existent records as not found\n\t\tfor (const record of records) {\n\t\t\tconst existing = existingRecords.find(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t);\n\t\t\tif (!existing) {\n\t\t\t\tresults.push({\n\t\t\t\t\trecord,\n\t\t\t\t\tdeleted: false,\n\t\t\t\t\tnotFound: true,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -6,7 +6,7 @@ const require_config = require("./config-CsYSyUya.cjs");
|
|
|
6
6
|
const require_workspace = require("./workspace-DEibcoBe.cjs");
|
|
7
7
|
const require_storage = require("./storage-CSGoC3bK.cjs");
|
|
8
8
|
const require_store = require("./store-DQgxT_WR.cjs");
|
|
9
|
-
const require_workspace$1 = require("./workspace-
|
|
9
|
+
const require_workspace$1 = require("./workspace-j6ZxvJqq.cjs");
|
|
10
10
|
const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-BN0PIZxd.cjs");
|
|
11
11
|
const require_openapi = require("./openapi.cjs");
|
|
12
12
|
const require_ready = require("./ready-lr2Fm-ep.cjs");
|
|
@@ -48,7 +48,7 @@ let _geekmidas_constructs_queue = require("@geekmidas/constructs/queue");
|
|
|
48
48
|
let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
|
|
49
49
|
let node_dns_promises = require("node:dns/promises");
|
|
50
50
|
//#region package.json
|
|
51
|
-
var version = "10.0.0-alpha.
|
|
51
|
+
var version = "10.0.0-alpha.50";
|
|
52
52
|
//#endregion
|
|
53
53
|
//#region src/auth/credentials.ts
|
|
54
54
|
/**
|
|
@@ -557,6 +557,181 @@ var UnsupportedPlatform = class extends Error {
|
|
|
557
557
|
}
|
|
558
558
|
};
|
|
559
559
|
//#endregion
|
|
560
|
+
//#region src/dev/appPorts.ts
|
|
561
|
+
/**
|
|
562
|
+
* Which local port each app answers on — and what to do when it is taken.
|
|
563
|
+
*
|
|
564
|
+
* An app's port used to be fixed: 3000, 3001, … and `gkm dev` refused to start
|
|
565
|
+
* if anything held one. That is right when the holder is this same app, left
|
|
566
|
+
* running by a previous `gkm dev` — moving would start a second copy beside it.
|
|
567
|
+
* It is wrong when the holder is another project's dev server, which also
|
|
568
|
+
* defaults to 3000: two projects could not run at once, and nothing about the
|
|
569
|
+
* app needs that port. A browser reaches it through the edge, whose URL carries
|
|
570
|
+
* no app port, and every address a phone or a route uses is derived on each
|
|
571
|
+
* start.
|
|
572
|
+
*
|
|
573
|
+
* So the holder is asked who it is. Every app process gkm starts is tagged with
|
|
574
|
+
* {@link APP_TAG_ENV} — the workspace and the app — and children inherit it, so
|
|
575
|
+
* whatever actually binds the port (`next-server`, a Metro worker, the tsx
|
|
576
|
+
* server) carries it. A held port whose holder carries this app's tag is a
|
|
577
|
+
* leftover, and is refused; anything else — untagged, another workspace's,
|
|
578
|
+
* unreadable — is not ours, and the app moves to the next free port.
|
|
579
|
+
*
|
|
580
|
+
* The choice is kept in `.gkm/app-ports.json`, not recomputed: an app that
|
|
581
|
+
* moved stays where it moved, so a bookmark or a phone's saved address does not
|
|
582
|
+
* break the next time the other project happens to be stopped. Local only —
|
|
583
|
+
* `gkm deploy` and `gkm docker` never read it.
|
|
584
|
+
*/
|
|
585
|
+
var appPorts_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
586
|
+
APP_TAG_ENV: () => APP_TAG_ENV,
|
|
587
|
+
appTag: () => appTag,
|
|
588
|
+
assignAppPorts: () => assignAppPorts,
|
|
589
|
+
describeHolder: () => describeHolder,
|
|
590
|
+
holderOf: () => holderOf,
|
|
591
|
+
savedAppPorts: () => savedAppPorts,
|
|
592
|
+
withAppPorts: () => withAppPorts
|
|
593
|
+
});
|
|
594
|
+
/** Carried by every app process gkm starts: `<workspace root>#<app>`. */
|
|
595
|
+
const APP_TAG_ENV = "GKM_DEV_APP";
|
|
596
|
+
const APP_PORTS_PATH = ".gkm/app-ports.json";
|
|
597
|
+
/** The tag an app's processes carry. The root is encoded, so it has no spaces. */
|
|
598
|
+
function appTag(root, app) {
|
|
599
|
+
return `${encodeURIComponent((0, node_path.resolve)(root))}#${app}`;
|
|
600
|
+
}
|
|
601
|
+
/** Says, as `command (pid N)`, what holds a port. */
|
|
602
|
+
function describeHolder(holder) {
|
|
603
|
+
if (!holder) return "another process";
|
|
604
|
+
return `${holder.command ?? "a process"} (pid ${holder.pid})`;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Who is listening on a port, read the way a person would: `lsof` for the
|
|
608
|
+
* pid, then that process's environment for its tag — `/proc/<pid>/environ` on
|
|
609
|
+
* Linux, `ps eww` elsewhere. Both work for the user's own processes, which is
|
|
610
|
+
* every process gkm starts; one that cannot be read has no tag, so is not ours.
|
|
611
|
+
*/
|
|
612
|
+
function holderOf(port) {
|
|
613
|
+
let out;
|
|
614
|
+
try {
|
|
615
|
+
out = (0, node_child_process.execFileSync)("lsof", [
|
|
616
|
+
"-nP",
|
|
617
|
+
`-iTCP:${port}`,
|
|
618
|
+
"-sTCP:LISTEN",
|
|
619
|
+
"-Fpc"
|
|
620
|
+
], {
|
|
621
|
+
encoding: "utf8",
|
|
622
|
+
stdio: [
|
|
623
|
+
"ignore",
|
|
624
|
+
"pipe",
|
|
625
|
+
"ignore"
|
|
626
|
+
]
|
|
627
|
+
});
|
|
628
|
+
} catch {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
const pid = Number(out.match(/^p(\d+)/m)?.[1]);
|
|
632
|
+
if (!pid) return void 0;
|
|
633
|
+
const command = out.match(/^c(.+)$/m)?.[1];
|
|
634
|
+
const tag = tagOf(pid);
|
|
635
|
+
return {
|
|
636
|
+
pid,
|
|
637
|
+
...command ? { command } : {},
|
|
638
|
+
...tag ? { tag } : {}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
function tagOf(pid) {
|
|
642
|
+
const prefix = `${APP_TAG_ENV}=`;
|
|
643
|
+
try {
|
|
644
|
+
return (process.platform === "linux" ? (0, node_fs.readFileSync)(`/proc/${pid}/environ`, "utf8").split("\0") : (0, node_child_process.execFileSync)("ps", [
|
|
645
|
+
"eww",
|
|
646
|
+
"-o",
|
|
647
|
+
"command=",
|
|
648
|
+
"-p",
|
|
649
|
+
String(pid)
|
|
650
|
+
], {
|
|
651
|
+
encoding: "utf8",
|
|
652
|
+
stdio: [
|
|
653
|
+
"ignore",
|
|
654
|
+
"pipe",
|
|
655
|
+
"ignore"
|
|
656
|
+
]
|
|
657
|
+
}).split(/\s+/)).find((entry) => entry.startsWith(prefix))?.slice(prefix.length);
|
|
658
|
+
} catch {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
/** The ports apps were given before, by app name. */
|
|
663
|
+
async function savedAppPorts(root) {
|
|
664
|
+
try {
|
|
665
|
+
return JSON.parse(await (0, node_fs_promises.readFile)((0, node_path.join)(root, APP_PORTS_PATH), "utf8"));
|
|
666
|
+
} catch {
|
|
667
|
+
return {};
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
async function saveAppPorts(root, ports) {
|
|
671
|
+
await (0, node_fs_promises.mkdir)((0, node_path.join)(root, ".gkm"), { recursive: true });
|
|
672
|
+
await (0, node_fs_promises.writeFile)((0, node_path.join)(root, APP_PORTS_PATH), `${JSON.stringify(ports, null, 2)}\n`);
|
|
673
|
+
}
|
|
674
|
+
/** The workspace, with each app on the port it was given locally. */
|
|
675
|
+
function withAppPorts(workspace, ports) {
|
|
676
|
+
const apps = Object.fromEntries(Object.entries(workspace.apps).map(([name, app]) => [name, ports[name] ? {
|
|
677
|
+
...app,
|
|
678
|
+
port: ports[name]
|
|
679
|
+
} : app]));
|
|
680
|
+
return {
|
|
681
|
+
...workspace,
|
|
682
|
+
apps
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Give each named app a local port: the one it had, or its usual one, unless
|
|
687
|
+
* another project holds it — then the next free one, which is kept.
|
|
688
|
+
*
|
|
689
|
+
* @returns every app's port, the apps that moved, and the apps this workspace
|
|
690
|
+
* already has running (the caller refuses to start those).
|
|
691
|
+
*/
|
|
692
|
+
async function assignAppPorts(workspace, names, deps) {
|
|
693
|
+
const saved = await savedAppPorts(workspace.root);
|
|
694
|
+
const wanted = {};
|
|
695
|
+
for (const name of names) {
|
|
696
|
+
const port = saved[name] ?? workspace.apps[name]?.port;
|
|
697
|
+
if (port) wanted[name] = port;
|
|
698
|
+
}
|
|
699
|
+
const taken = /* @__PURE__ */ new Set([...Object.values(saved), ...Object.values(wanted)]);
|
|
700
|
+
const ports = { ...saved };
|
|
701
|
+
const moved = [];
|
|
702
|
+
const running = [];
|
|
703
|
+
for (const name of Object.keys(wanted).sort()) {
|
|
704
|
+
const want = wanted[name];
|
|
705
|
+
ports[name] = want;
|
|
706
|
+
if (await deps.free(want)) continue;
|
|
707
|
+
const holder = deps.holder(want);
|
|
708
|
+
if (holder?.tag === appTag(workspace.root, name)) {
|
|
709
|
+
running.push({
|
|
710
|
+
app: name,
|
|
711
|
+
port: want,
|
|
712
|
+
holder
|
|
713
|
+
});
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
let next = want + 1;
|
|
717
|
+
while (taken.has(next) || !await deps.free(next)) next += 1;
|
|
718
|
+
taken.add(next);
|
|
719
|
+
ports[name] = next;
|
|
720
|
+
moved.push({
|
|
721
|
+
app: name,
|
|
722
|
+
from: want,
|
|
723
|
+
to: next,
|
|
724
|
+
holder
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
await saveAppPorts(workspace.root, ports);
|
|
728
|
+
return {
|
|
729
|
+
ports,
|
|
730
|
+
moved,
|
|
731
|
+
running
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
//#endregion
|
|
560
735
|
//#region ../../node_modules/.pnpm/@hono+node-server@1.19.5_hono@4.13.8/node_modules/@hono/node-server/dist/index.mjs
|
|
561
736
|
var RequestError = class extends Error {
|
|
562
737
|
constructor(message, options) {
|
|
@@ -1192,6 +1367,7 @@ async function devCommand(options) {
|
|
|
1192
1367
|
throw error;
|
|
1193
1368
|
});
|
|
1194
1369
|
if (!appConfig) {
|
|
1370
|
+
logger$9.log("🔎 Reading gkm.config.ts and the constructs");
|
|
1195
1371
|
const { workspace: everything } = await require_config.loadWorkspaceConfig();
|
|
1196
1372
|
return workspaceDevCommand(everything, options);
|
|
1197
1373
|
}
|
|
@@ -1199,8 +1375,15 @@ async function devCommand(options) {
|
|
|
1199
1375
|
const appRoot = appConfig.appRoot;
|
|
1200
1376
|
const secretsRoot = appConfig.workspaceRoot;
|
|
1201
1377
|
const workspaceAppName = appConfig.appName;
|
|
1202
|
-
const
|
|
1203
|
-
|
|
1378
|
+
const assigned = await assignAppPorts(appConfig.workspace, [workspaceAppName], {
|
|
1379
|
+
free: require_workspace$1.isPortAvailable,
|
|
1380
|
+
holder: holderOf
|
|
1381
|
+
});
|
|
1382
|
+
if (assigned.running.length > 0) throw new WorkspacePortsInUse(assigned.running);
|
|
1383
|
+
for (const { app, from, to, holder } of assigned.moved) logger$9.log(`↪️ ${app}: ${from} is held by ${describeHolder(holder)}, which is not this workspace's — using ${to}`);
|
|
1384
|
+
const workspace = withAppPorts(appConfig.workspace, assigned.ports);
|
|
1385
|
+
const workspaceAppPort = assigned.ports[workspaceAppName] ?? appConfig.app.port;
|
|
1386
|
+
process.env[APP_TAG_ENV] = appTag(appConfig.workspaceRoot, workspaceAppName);
|
|
1204
1387
|
if (appConfig.app.entry) {
|
|
1205
1388
|
logger$9.log(`📄 Using entry point: ${appConfig.app.entry}`);
|
|
1206
1389
|
return entryDevCommand({
|
|
@@ -1233,6 +1416,7 @@ async function devCommand(options) {
|
|
|
1233
1416
|
bustCache,
|
|
1234
1417
|
serveEmpty: true
|
|
1235
1418
|
}));
|
|
1419
|
+
logger$9.log(`🔨 Building ${workspaceAppName ?? "the app"}`);
|
|
1236
1420
|
const initial = await build();
|
|
1237
1421
|
const runtime = config.runtime ?? "node";
|
|
1238
1422
|
let secretsJsonPath;
|
|
@@ -1241,7 +1425,8 @@ async function devCommand(options) {
|
|
|
1241
1425
|
if (workspace) {
|
|
1242
1426
|
const reconciled = await require_workspace$1.reconcileWorkspace(workspace, {
|
|
1243
1427
|
stage: workspace.stages.local,
|
|
1244
|
-
...options.fake ? { fake: true } : {}
|
|
1428
|
+
...options.fake ? { fake: true } : {},
|
|
1429
|
+
progress: (message) => logger$9.log(message)
|
|
1245
1430
|
});
|
|
1246
1431
|
if (reconciled.services.length > 0) {
|
|
1247
1432
|
logger$9.log("🐳 Services");
|
|
@@ -1500,7 +1685,14 @@ async function loadDevSecrets(workspace) {
|
|
|
1500
1685
|
* 3. Generate dependency URLs ({APP_NAME}_URL)
|
|
1501
1686
|
* 4. Spawn turbo run dev with injected env vars
|
|
1502
1687
|
*/
|
|
1503
|
-
async function workspaceDevCommand(
|
|
1688
|
+
async function workspaceDevCommand(configured, options) {
|
|
1689
|
+
const assigned = await assignAppPorts(configured, options.app ? [options.app] : options.filter ? [] : Object.keys(configured.apps), {
|
|
1690
|
+
free: require_workspace$1.isPortAvailable,
|
|
1691
|
+
holder: holderOf
|
|
1692
|
+
});
|
|
1693
|
+
if (assigned.running.length > 0) throw new WorkspacePortsInUse(assigned.running);
|
|
1694
|
+
for (const { app, from, to, holder } of assigned.moved) logger$9.log(`↪️ ${app}: ${from} is held by ${describeHolder(holder)}, which is not this workspace's — using ${to}`);
|
|
1695
|
+
const workspace = withAppPorts(configured, assigned.ports);
|
|
1504
1696
|
const appCount = Object.keys(workspace.apps).length;
|
|
1505
1697
|
const frontendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "web");
|
|
1506
1698
|
const conflicts = checkPortConflicts(workspace);
|
|
@@ -1524,7 +1716,8 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1524
1716
|
const rawSecrets = await loadDevSecrets(workspace);
|
|
1525
1717
|
const reconciled = await require_workspace$1.reconcileWorkspace(workspace, {
|
|
1526
1718
|
stage: workspace.stages.local,
|
|
1527
|
-
...options.fake ? { fake: true } : {}
|
|
1719
|
+
...options.fake ? { fake: true } : {},
|
|
1720
|
+
progress: (message) => logger$9.log(message)
|
|
1528
1721
|
});
|
|
1529
1722
|
if (reconciled.services.length > 0) {
|
|
1530
1723
|
logger$9.log("🐳 Services");
|
|
@@ -1536,10 +1729,11 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1536
1729
|
};
|
|
1537
1730
|
const fakes = options.fake ? await serveFakes(reconciled.fakes, reconciled.ports) : [];
|
|
1538
1731
|
for (const { id, port } of fakes) logger$9.log(`🎭 ${id} fake: http://localhost:${port}`);
|
|
1732
|
+
logger$9.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
|
|
1539
1733
|
if (options.migrate || options.seed) {
|
|
1540
|
-
const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-
|
|
1734
|
+
const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-CNlew9Ek.cjs"));
|
|
1541
1735
|
await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
|
|
1542
|
-
} else await Promise.resolve().then(() => require("./migrate-
|
|
1736
|
+
} else await Promise.resolve().then(() => require("./migrate-CNlew9Ek.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
|
|
1543
1737
|
logger$9.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
|
|
1544
1738
|
});
|
|
1545
1739
|
const appUrls = {};
|
|
@@ -1601,17 +1795,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1601
1795
|
...configPath ? { GKM_CONFIG_PATH: configPath } : {},
|
|
1602
1796
|
...options.fake ? { [require_workspace$1.FAKE_ENV]: "1" } : {}
|
|
1603
1797
|
};
|
|
1604
|
-
|
|
1605
|
-
const held = [];
|
|
1606
|
-
for (const appName of scoped) {
|
|
1607
|
-
const port = workspace.apps[appName]?.port;
|
|
1608
|
-
if (port && !await require_workspace$1.isPortAvailable(port)) held.push({
|
|
1609
|
-
app: appName,
|
|
1610
|
-
port,
|
|
1611
|
-
holder: listenerOn(port)
|
|
1612
|
-
});
|
|
1613
|
-
}
|
|
1614
|
-
if (held.length > 0) throw new WorkspacePortsInUse(held);
|
|
1798
|
+
logger$9.log(`🚀 Starting ${appCount} app(s) — each builds before it answers, and says so when it is ready`);
|
|
1615
1799
|
const turboProcess = (0, node_child_process.spawn)("pnpm", [
|
|
1616
1800
|
"turbo",
|
|
1617
1801
|
"run",
|
|
@@ -1906,10 +2090,16 @@ var DevPortInUse = class extends Error {
|
|
|
1906
2090
|
}
|
|
1907
2091
|
};
|
|
1908
2092
|
/** Apps whose provisioned ports are held before the workspace starts. */
|
|
2093
|
+
/**
|
|
2094
|
+
* This workspace's own apps are already running — left by a previous start.
|
|
2095
|
+
*
|
|
2096
|
+
* Only these are refused: a port another project holds is moved off instead
|
|
2097
|
+
* (see `assignAppPorts`), but moving off our own would start a second copy.
|
|
2098
|
+
*/
|
|
1909
2099
|
var WorkspacePortsInUse = class extends Error {
|
|
1910
2100
|
held;
|
|
1911
2101
|
constructor(held) {
|
|
1912
|
-
super(`
|
|
2102
|
+
super(`This workspace's apps are already running:\n${held.map(({ app, port, holder }) => ` ${app}: ${port} — ${describeHolder(holder)}`).join("\n")}\nA previous \`gkm dev\` left them behind. Stop them (kill the pids above) and start again. Nothing was started.`);
|
|
1913
2103
|
this.held = held;
|
|
1914
2104
|
this.name = "WorkspacePortsInUse";
|
|
1915
2105
|
}
|
|
@@ -9138,26 +9328,26 @@ function loadPackageJson() {
|
|
|
9138
9328
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
9139
9329
|
*/
|
|
9140
9330
|
const GEEKMIDAS_VERSIONS = {
|
|
9141
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
9142
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
9143
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
9144
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
9145
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
9146
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
9147
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
9148
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
9149
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
9150
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
9151
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
9152
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
9153
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
9154
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
9155
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
9156
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
9157
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
9158
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
9159
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
9160
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
9331
|
+
"@geekmidas/audit": "~10.0.0-alpha.50",
|
|
9332
|
+
"@geekmidas/auth": "~10.0.0-alpha.50",
|
|
9333
|
+
"@geekmidas/cache": "~10.0.0-alpha.50",
|
|
9334
|
+
"@geekmidas/client": "~10.0.0-alpha.50",
|
|
9335
|
+
"@geekmidas/cloud": "~10.0.0-alpha.50",
|
|
9336
|
+
"@geekmidas/constructs": "~10.0.0-alpha.50",
|
|
9337
|
+
"@geekmidas/db": "~10.0.0-alpha.50",
|
|
9338
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.50",
|
|
9339
|
+
"@geekmidas/envkit": "~10.0.0-alpha.50",
|
|
9340
|
+
"@geekmidas/errors": "~10.0.0-alpha.50",
|
|
9341
|
+
"@geekmidas/events": "~10.0.0-alpha.50",
|
|
9342
|
+
"@geekmidas/logger": "~10.0.0-alpha.50",
|
|
9343
|
+
"@geekmidas/manifest": "~10.0.0-alpha.50",
|
|
9344
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.50",
|
|
9345
|
+
"@geekmidas/schema": "~10.0.0-alpha.50",
|
|
9346
|
+
"@geekmidas/services": "~10.0.0-alpha.50",
|
|
9347
|
+
"@geekmidas/storage": "~10.0.0-alpha.50",
|
|
9348
|
+
"@geekmidas/studio": "~10.0.0-alpha.50",
|
|
9349
|
+
"@geekmidas/telescope": "~10.0.0-alpha.50",
|
|
9350
|
+
"@geekmidas/testkit": "~10.0.0-alpha.50",
|
|
9161
9351
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
9162
9352
|
};
|
|
9163
9353
|
//#endregion
|
|
@@ -15900,7 +16090,7 @@ function testDatabases(reconciled) {
|
|
|
15900
16090
|
* starts without the reference data the app needs.
|
|
15901
16091
|
*/
|
|
15902
16092
|
async function migrateTestStage(workspace, env) {
|
|
15903
|
-
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-
|
|
16093
|
+
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-CNlew9Ek.cjs"));
|
|
15904
16094
|
const { discover } = await Promise.resolve().then(() => require("./discover-DXNviAh-.cjs")).then((n) => n.discover_exports);
|
|
15905
16095
|
const sources = {};
|
|
15906
16096
|
const manifest = await discover({
|
|
@@ -16331,7 +16521,7 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
16331
16521
|
try {
|
|
16332
16522
|
const globalOptions = program.opts();
|
|
16333
16523
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16334
|
-
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16524
|
+
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-CNlew9Ek.cjs"));
|
|
16335
16525
|
await migrateCommand({
|
|
16336
16526
|
...options,
|
|
16337
16527
|
...construct ? { construct } : {}
|
|
@@ -16345,7 +16535,7 @@ program.command("seed").description("Migrate, then run each database construct's
|
|
|
16345
16535
|
try {
|
|
16346
16536
|
const globalOptions = program.opts();
|
|
16347
16537
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16348
|
-
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16538
|
+
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-CNlew9Ek.cjs"));
|
|
16349
16539
|
await seedCommand({
|
|
16350
16540
|
...options,
|
|
16351
16541
|
...construct ? { construct } : {}
|
|
@@ -16359,7 +16549,7 @@ program.command("migration").description("Write a construct's next migration: an
|
|
|
16359
16549
|
try {
|
|
16360
16550
|
const globalOptions = program.opts();
|
|
16361
16551
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16362
|
-
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16552
|
+
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-CNlew9Ek.cjs"));
|
|
16363
16553
|
await migrationCommand({
|
|
16364
16554
|
construct,
|
|
16365
16555
|
...name ? { name } : {}
|
|
@@ -16520,7 +16710,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
16520
16710
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
16521
16711
|
process.exit(1);
|
|
16522
16712
|
}
|
|
16523
|
-
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-
|
|
16713
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-j6ZxvJqq.cjs")).then((n) => n.workspace_exports);
|
|
16524
16714
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
16525
16715
|
if (!result) {
|
|
16526
16716
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -16704,6 +16894,12 @@ program.command("upgrade").description("Upgrade @geekmidas/cli (with --all, ever
|
|
|
16704
16894
|
});
|
|
16705
16895
|
program.parse();
|
|
16706
16896
|
//#endregion
|
|
16897
|
+
Object.defineProperty(exports, "appPorts_exports", {
|
|
16898
|
+
enumerable: true,
|
|
16899
|
+
get: function() {
|
|
16900
|
+
return appPorts_exports;
|
|
16901
|
+
}
|
|
16902
|
+
});
|
|
16707
16903
|
Object.defineProperty(exports, "encryption_exports", {
|
|
16708
16904
|
enumerable: true,
|
|
16709
16905
|
get: function() {
|