@hasna/domains 0.0.39 → 0.0.42
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 +11 -15
- package/dist/cli/commands/domain.d.ts.map +1 -1
- package/dist/cli/index.js +19736 -32040
- package/dist/db/database.d.ts +24 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/domain-records.d.ts +61 -2
- package/dist/db/domain-records.d.ts.map +1 -1
- package/dist/db/domains.d.ts +14 -7
- package/dist/db/domains.d.ts.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/pg-migrations.d.ts.map +1 -1
- package/dist/db/store.d.ts +48 -48
- package/dist/db/store.d.ts.map +1 -1
- package/dist/generated/storage-kit/backend.d.ts +19 -0
- package/dist/generated/storage-kit/backend.d.ts.map +1 -0
- package/dist/generated/storage-kit/index.d.ts +2 -2
- package/dist/generated/storage-kit/index.d.ts.map +1 -1
- package/dist/generated/storage-kit/migrations.d.ts.map +1 -1
- package/dist/generated/storage-kit/own.d.ts +11 -0
- package/dist/generated/storage-kit/own.d.ts.map +1 -0
- package/dist/generated/storage-kit/pool.d.ts +7 -6
- package/dist/generated/storage-kit/pool.d.ts.map +1 -1
- package/dist/generated/storage-kit/query.d.ts +1 -1
- package/dist/generated/storage-kit/query.d.ts.map +1 -1
- package/dist/generated/storage-kit/tls.d.ts +30 -3
- package/dist/generated/storage-kit/tls.d.ts.map +1 -1
- package/dist/index.js +15865 -28359
- package/dist/lib/brandsight.d.ts.map +1 -1
- package/dist/lib/config.d.ts +23 -3
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/freshness.d.ts +65 -0
- package/dist/lib/freshness.d.ts.map +1 -0
- package/dist/lib/godaddy.d.ts.map +1 -1
- package/dist/lib/namecheap.d.ts +2 -0
- package/dist/lib/namecheap.d.ts.map +1 -1
- package/dist/lib/registrar.js +17 -6
- package/dist/lib/route53.d.ts.map +1 -1
- package/dist/lib/route53.js +5 -2
- package/dist/mcp/index.js +13479 -25921
- package/dist/sdk/index.d.ts +3 -2
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +280 -3
- package/dist/server/app.d.ts +12 -8
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/index.d.ts +4 -5
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +527 -181
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/repo.d.ts.map +1 -1
- package/package.json +7 -4
- package/dist/generated/storage-kit/mode.d.ts +0 -48
- package/dist/generated/storage-kit/mode.d.ts.map +0 -1
package/dist/sdk/index.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @hasna/domains SDK — typed HTTP client generated from the domains-serve
|
|
3
3
|
* OpenAPI document (see `src/server/openapi.ts`, regenerate via the `sdk:gen`
|
|
4
|
-
* script). Client
|
|
4
|
+
* script). Client hosted convention: `DOMAINS_API_URL` + `DOMAINS_API_KEY`
|
|
5
5
|
* (a key, never a DSN).
|
|
6
6
|
*/
|
|
7
7
|
export * from "./client.js";
|
|
8
8
|
import { DomainsClient, type DomainsClientOptions } from "./client.js";
|
|
9
9
|
/**
|
|
10
10
|
* Build a `DomainsClient` from the environment.
|
|
11
|
-
*
|
|
11
|
+
* Resolves `DOMAINS_API_URL` (or `HASNA_DOMAINS_API_URL`) and the API key
|
|
12
|
+
* through the @hasna/contracts client credential chain.
|
|
12
13
|
*/
|
|
13
14
|
export declare function createDomainsClientFromEnv(env?: NodeJS.ProcessEnv, overrides?: Partial<DomainsClientOptions>): DomainsClient;
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/sdk/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEvE;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,aAAa,CAOf"}
|
package/dist/sdk/index.js
CHANGED
|
@@ -160,14 +160,291 @@ class DomainsClient {
|
|
|
160
160
|
});
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
+
// ../contracts/dist/client/transport.js
|
|
164
|
+
import { readFileSync, statSync } from "fs";
|
|
165
|
+
import { join } from "path";
|
|
166
|
+
function envToken(name) {
|
|
167
|
+
return name.toUpperCase().replace(/-/g, "_");
|
|
168
|
+
}
|
|
169
|
+
function clientTransportEnvKeys(name) {
|
|
170
|
+
const envSegment = envToken(name);
|
|
171
|
+
return {
|
|
172
|
+
apiUrlKeys: [`HASNA_${envSegment}_API_URL`, `${envSegment}_API_URL`],
|
|
173
|
+
apiKeyKeys: [`HASNA_${envSegment}_API_KEY`, `${envSegment}_API_KEY`]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function credentialOverrideEnvKey(name) {
|
|
177
|
+
return `HASNA_${envToken(name)}_API_KEY_OVERRIDE`;
|
|
178
|
+
}
|
|
179
|
+
var CREDENTIAL_PROFILE_ENV_KEY = "HASNA_PROFILE";
|
|
180
|
+
|
|
181
|
+
class CredentialResolutionError extends Error {
|
|
182
|
+
appName;
|
|
183
|
+
attempted;
|
|
184
|
+
constructor(appName, message, attempted) {
|
|
185
|
+
super(message);
|
|
186
|
+
this.name = "CredentialResolutionError";
|
|
187
|
+
this.appName = appName;
|
|
188
|
+
this.attempted = attempted;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
var HASNA_STATE_DIR = ".hasna";
|
|
192
|
+
var FLEET_CREDENTIAL_DIR = "cloud";
|
|
193
|
+
var CONFIG_DIR = ".config";
|
|
194
|
+
var CONFIG_NAMESPACE = "hasna";
|
|
195
|
+
var MAX_CREDENTIAL_FILE_BYTES = 64 * 1024;
|
|
196
|
+
var SAFE_APP_SLUG = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
197
|
+
var SAFE_PROFILE = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
|
|
198
|
+
var ILLEGAL_IN_HEADER_VALUE = /[^\t\x20-\x7e]/;
|
|
199
|
+
function homeDir(env) {
|
|
200
|
+
const home = env.HOME?.trim();
|
|
201
|
+
return home ? home : null;
|
|
202
|
+
}
|
|
203
|
+
function credentialDiskSources(name, env) {
|
|
204
|
+
return profileDiskSources(name, env, null);
|
|
205
|
+
}
|
|
206
|
+
function profileDiskSources(name, env, profile) {
|
|
207
|
+
const home = homeDir(env);
|
|
208
|
+
if (!home || !SAFE_APP_SLUG.test(name))
|
|
209
|
+
return [];
|
|
210
|
+
const stem = profile ? `${name}.${profile}` : name;
|
|
211
|
+
const configStem = profile ? `${name}-${profile}` : name;
|
|
212
|
+
return [
|
|
213
|
+
join(home, HASNA_STATE_DIR, FLEET_CREDENTIAL_DIR, `${stem}.env`),
|
|
214
|
+
join(home, CONFIG_DIR, CONFIG_NAMESPACE, `${configStem}-cloud.env`)
|
|
215
|
+
];
|
|
216
|
+
}
|
|
217
|
+
function parseEnvFile(text) {
|
|
218
|
+
const values = new Map;
|
|
219
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
220
|
+
const line = rawLine.trim();
|
|
221
|
+
if (line.length === 0 || line.startsWith("#"))
|
|
222
|
+
continue;
|
|
223
|
+
const withoutExport = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
|
|
224
|
+
const equals = withoutExport.indexOf("=");
|
|
225
|
+
if (equals <= 0)
|
|
226
|
+
continue;
|
|
227
|
+
const key = withoutExport.slice(0, equals).trim();
|
|
228
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
229
|
+
continue;
|
|
230
|
+
let value = withoutExport.slice(equals + 1).trim();
|
|
231
|
+
const quote = value[0];
|
|
232
|
+
if (quote === '"' || quote === "'") {
|
|
233
|
+
if (value.length < 2 || !value.endsWith(quote))
|
|
234
|
+
continue;
|
|
235
|
+
value = value.slice(1, -1);
|
|
236
|
+
}
|
|
237
|
+
if (value.length === 0)
|
|
238
|
+
continue;
|
|
239
|
+
values.set(key, value);
|
|
240
|
+
}
|
|
241
|
+
return values;
|
|
242
|
+
}
|
|
243
|
+
function readAppConfigFile(path) {
|
|
244
|
+
let text;
|
|
245
|
+
try {
|
|
246
|
+
const stats = statSync(path);
|
|
247
|
+
if (!stats.isFile() || stats.size > MAX_CREDENTIAL_FILE_BYTES)
|
|
248
|
+
return null;
|
|
249
|
+
text = readFileSync(path, "utf8");
|
|
250
|
+
} catch {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
return parseEnvFile(text);
|
|
254
|
+
}
|
|
255
|
+
function readCredentialFile(path, apiKeyKeys) {
|
|
256
|
+
const values = readAppConfigFile(path);
|
|
257
|
+
if (!values)
|
|
258
|
+
return null;
|
|
259
|
+
for (const key of apiKeyKeys) {
|
|
260
|
+
const value = values.get(key)?.trim();
|
|
261
|
+
if (value)
|
|
262
|
+
return value;
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
function assertUsableCredential(appName, source, value) {
|
|
267
|
+
if (!ILLEGAL_IN_HEADER_VALUE.test(value))
|
|
268
|
+
return;
|
|
269
|
+
throw new CredentialResolutionError(appName, `The credential from ${source} contains characters that cannot be sent in an HTTP header ` + `(a control character or non-ASCII byte). A file written with CR-only line endings is the usual ` + `cause. Rewrite that credential file with one LF-terminated KEY=value line. ` + `The value is not shown here, and is deliberately never logged.`, [source]);
|
|
270
|
+
}
|
|
271
|
+
var INSPECT_CUSTOM = Symbol.for("nodejs.util.inspect.custom");
|
|
272
|
+
var CREDENTIAL_SEAL = Symbol.for("hasna:contracts:sealedCredential");
|
|
273
|
+
function sealCredential(fields) {
|
|
274
|
+
const { apiKey } = fields;
|
|
275
|
+
const visible = {
|
|
276
|
+
tier: fields.tier,
|
|
277
|
+
source: fields.source,
|
|
278
|
+
deliberate: fields.deliberate,
|
|
279
|
+
deprecated: fields.deprecated,
|
|
280
|
+
diskCandidates: Object.freeze([...fields.diskCandidates]),
|
|
281
|
+
warning: fields.warning
|
|
282
|
+
};
|
|
283
|
+
const sealed = { ...visible };
|
|
284
|
+
Object.defineProperty(sealed, "apiKey", {
|
|
285
|
+
value: apiKey,
|
|
286
|
+
enumerable: false,
|
|
287
|
+
writable: false,
|
|
288
|
+
configurable: false
|
|
289
|
+
});
|
|
290
|
+
Object.defineProperty(sealed, INSPECT_CUSTOM, {
|
|
291
|
+
value: () => ({ ...visible, apiKey: "[redacted]" }),
|
|
292
|
+
enumerable: false,
|
|
293
|
+
writable: false,
|
|
294
|
+
configurable: false
|
|
295
|
+
});
|
|
296
|
+
Object.defineProperty(sealed, CREDENTIAL_SEAL, {
|
|
297
|
+
value: true,
|
|
298
|
+
enumerable: false,
|
|
299
|
+
writable: false,
|
|
300
|
+
configurable: false
|
|
301
|
+
});
|
|
302
|
+
return Object.freeze(sealed);
|
|
303
|
+
}
|
|
304
|
+
function firstEnvValue(env, keys) {
|
|
305
|
+
for (const key of keys) {
|
|
306
|
+
const value = env[key]?.trim();
|
|
307
|
+
if (value)
|
|
308
|
+
return { key, value };
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
var DEPRECATION_REGISTRY = Symbol.for("hasna:contracts:credentialDeprecationNotices");
|
|
313
|
+
function deprecationNotified() {
|
|
314
|
+
const host = globalThis;
|
|
315
|
+
const existing = host[DEPRECATION_REGISTRY];
|
|
316
|
+
if (existing instanceof Set)
|
|
317
|
+
return existing;
|
|
318
|
+
const created = new Set;
|
|
319
|
+
host[DEPRECATION_REGISTRY] = created;
|
|
320
|
+
return created;
|
|
321
|
+
}
|
|
322
|
+
function defaultDeprecationSink(message) {
|
|
323
|
+
if (typeof process !== "undefined" && process.stderr) {
|
|
324
|
+
process.stderr.write(`${message}
|
|
325
|
+
`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function resolveCredential(name, env, options = {}) {
|
|
329
|
+
const { apiKeyKeys } = clientTransportEnvKeys(name);
|
|
330
|
+
const diskPaths = credentialDiskSources(name, env);
|
|
331
|
+
const explicitKey = options.apiKey?.trim();
|
|
332
|
+
if (explicitKey) {
|
|
333
|
+
assertUsableCredential(name, "the explicit apiKey argument", explicitKey);
|
|
334
|
+
return sealCredential({
|
|
335
|
+
apiKey: explicitKey,
|
|
336
|
+
tier: "argument",
|
|
337
|
+
source: "explicit apiKey argument",
|
|
338
|
+
deliberate: true,
|
|
339
|
+
deprecated: false,
|
|
340
|
+
diskCandidates: diskPaths,
|
|
341
|
+
warning: null
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
const overrideKeyName = credentialOverrideEnvKey(name);
|
|
345
|
+
const overrideRaw = env[overrideKeyName];
|
|
346
|
+
if (overrideRaw !== undefined) {
|
|
347
|
+
const override = overrideRaw.trim();
|
|
348
|
+
if (!override) {
|
|
349
|
+
throw new CredentialResolutionError(name, `${overrideKeyName} is set but empty. It is a deliberate override, so it is not resolved around: ` + `either give it a real key or unset it to fall back to the credential on disk.`, [overrideKeyName]);
|
|
350
|
+
}
|
|
351
|
+
assertUsableCredential(name, overrideKeyName, override);
|
|
352
|
+
return sealCredential({
|
|
353
|
+
apiKey: override,
|
|
354
|
+
tier: "override",
|
|
355
|
+
source: overrideKeyName,
|
|
356
|
+
deliberate: true,
|
|
357
|
+
deprecated: false,
|
|
358
|
+
diskCandidates: diskPaths,
|
|
359
|
+
warning: null
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
const profile = options.profile?.trim() || env[CREDENTIAL_PROFILE_ENV_KEY]?.trim();
|
|
363
|
+
if (profile) {
|
|
364
|
+
const profileSource = options.profile?.trim() ? "explicit profile argument" : CREDENTIAL_PROFILE_ENV_KEY;
|
|
365
|
+
if (!SAFE_PROFILE.test(profile)) {
|
|
366
|
+
throw new CredentialResolutionError(name, `Profile name from ${profileSource} is not usable in a path. ` + `Use letters, digits, dot, dash, or underscore.`, [profileSource]);
|
|
367
|
+
}
|
|
368
|
+
const paths = profileDiskSources(name, env, profile);
|
|
369
|
+
for (const path of paths) {
|
|
370
|
+
const value = readCredentialFile(path, apiKeyKeys);
|
|
371
|
+
if (value) {
|
|
372
|
+
assertUsableCredential(name, path, value);
|
|
373
|
+
return sealCredential({
|
|
374
|
+
apiKey: value,
|
|
375
|
+
tier: "profile",
|
|
376
|
+
source: path,
|
|
377
|
+
deliberate: true,
|
|
378
|
+
deprecated: false,
|
|
379
|
+
diskCandidates: paths,
|
|
380
|
+
warning: null
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
throw new CredentialResolutionError(name, `Profile '${profile}' (from ${profileSource}) has no ${apiKeyKeys[0]} for '${name}'. ` + `Looked in: ${paths.join(", ") || "<no HOME in this environment>"}. ` + `A profile names WHICH identity to use, so it is never resolved around \u2014 ` + `create the profile's credential file or unset ${CREDENTIAL_PROFILE_ENV_KEY}.`, paths);
|
|
385
|
+
}
|
|
386
|
+
const diskHits = diskPaths.map((path) => ({ path, value: readCredentialFile(path, apiKeyKeys) })).filter((hit) => hit.value !== null);
|
|
387
|
+
if (diskHits.length > 0) {
|
|
388
|
+
const winner = diskHits[0];
|
|
389
|
+
assertUsableCredential(name, winner.path, winner.value);
|
|
390
|
+
const divergentSources = [
|
|
391
|
+
...diskHits.slice(1).filter((hit) => hit.value !== winner.value).map((hit) => hit.path),
|
|
392
|
+
...(() => {
|
|
393
|
+
const legacyHit = firstEnvValue(env, apiKeyKeys);
|
|
394
|
+
return legacyHit && legacyHit.value !== winner.value ? [legacyHit.key] : [];
|
|
395
|
+
})()
|
|
396
|
+
];
|
|
397
|
+
const warning = divergentSources.length > 0 ? `Credential sources disagree for '${name}': ${winner.path} and ` + `${divergentSources.join(", ")} hold different keys. ${winner.path} wins, because a file on ` + `disk is re-read on every call while an environment variable is a snapshot. Reconcile them \u2014 ` + `a rotation that updated only one leaves the other to fail 401 wherever it is loaded first.` : null;
|
|
398
|
+
return sealCredential({
|
|
399
|
+
apiKey: winner.value,
|
|
400
|
+
tier: "disk",
|
|
401
|
+
source: winner.path,
|
|
402
|
+
deliberate: false,
|
|
403
|
+
deprecated: false,
|
|
404
|
+
diskCandidates: diskPaths,
|
|
405
|
+
warning
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
const legacy = firstEnvValue(env, apiKeyKeys);
|
|
409
|
+
if (legacy) {
|
|
410
|
+
assertUsableCredential(name, legacy.key, legacy.value);
|
|
411
|
+
const where = diskPaths.length > 0 ? `Put the current key in ${diskPaths[0]} \u2014 it is re-read on every call, so rotations take effect immediately.` : `This environment has no HOME, so no credential file could be consulted at all; the disk tier is ` + `unavailable here and this process will keep using the environment snapshot.`;
|
|
412
|
+
const message = `[${name}] DEPRECATED: the API key came from ${legacy.key} in this process's environment. ` + `Environment variables are a snapshot taken when this process started, so a shell that started ` + `before a key rotation keeps using the old key until it exits. ${where}`;
|
|
413
|
+
const sink = options.onDeprecation ?? defaultDeprecationSink;
|
|
414
|
+
const notified = deprecationNotified();
|
|
415
|
+
if (!notified.has(name)) {
|
|
416
|
+
notified.add(name);
|
|
417
|
+
sink(message);
|
|
418
|
+
}
|
|
419
|
+
return sealCredential({
|
|
420
|
+
apiKey: legacy.value,
|
|
421
|
+
tier: "legacy-env",
|
|
422
|
+
source: legacy.key,
|
|
423
|
+
deliberate: false,
|
|
424
|
+
deprecated: true,
|
|
425
|
+
diskCandidates: diskPaths,
|
|
426
|
+
warning: message
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
var IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
432
|
+
var AUTHORITY_OVERRIDE_HEADERS = new Set([
|
|
433
|
+
"host",
|
|
434
|
+
":authority",
|
|
435
|
+
"forwarded",
|
|
436
|
+
"x-forwarded-host",
|
|
437
|
+
"x-original-host"
|
|
438
|
+
]);
|
|
439
|
+
|
|
163
440
|
// src/sdk/index.ts
|
|
164
441
|
function createDomainsClientFromEnv(env = process.env, overrides = {}) {
|
|
165
|
-
const baseUrl = overrides.baseUrl ?? env["DOMAINS_API_URL"];
|
|
442
|
+
const baseUrl = overrides.baseUrl ?? env["DOMAINS_API_URL"] ?? env["HASNA_DOMAINS_API_URL"];
|
|
166
443
|
if (!baseUrl) {
|
|
167
444
|
throw new Error("createDomainsClientFromEnv requires DOMAINS_API_URL (the domains-serve base URL).");
|
|
168
445
|
}
|
|
169
|
-
const
|
|
170
|
-
return new DomainsClient({ baseUrl, ...apiKey ? { apiKey } : {}, ...overrides });
|
|
446
|
+
const resolved = resolveCredential("domains", env, { apiKey: overrides.apiKey });
|
|
447
|
+
return new DomainsClient({ baseUrl, ...resolved?.apiKey ? { apiKey: resolved.apiKey } : {}, ...overrides });
|
|
171
448
|
}
|
|
172
449
|
export {
|
|
173
450
|
createDomainsClientFromEnv,
|
package/dist/server/app.d.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* domains-serve HTTP application — framework-agnostic request handler.
|
|
3
3
|
*
|
|
4
4
|
* Surfaces:
|
|
5
|
-
* GET /health — liveness ({status,version
|
|
5
|
+
* GET /health — liveness ({status,version})
|
|
6
6
|
* GET /ready — readiness (DB reachable + schema migrated)
|
|
7
|
-
* GET /version — {status,version
|
|
7
|
+
* GET /version — {status,version}
|
|
8
8
|
* GET /openapi.json — the OpenAPI 3.1 document
|
|
9
9
|
* /v1/* — API-key authenticated CRUD over the portfolio
|
|
10
10
|
*
|
|
@@ -12,18 +12,22 @@
|
|
|
12
12
|
* scope grammar `domains:<action>`, revocation via the ApiKeyStore). Reads need
|
|
13
13
|
* `domains:read`; writes need `domains:write`. A `domains:*` key covers both.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* Every request reads/writes the PostgreSQL backend directly. No cache, no
|
|
16
|
+
* local mirror.
|
|
17
17
|
*/
|
|
18
|
-
import { type AuthAuditEvent } from "@hasna/contracts/auth";
|
|
18
|
+
import { type AuthAuditEvent, type KeyStatusResolver } from "@hasna/contracts/auth";
|
|
19
19
|
import type { TypedQueryClient } from "../generated/storage-kit/index.js";
|
|
20
20
|
export interface ServeAppOptions {
|
|
21
21
|
db: TypedQueryClient;
|
|
22
22
|
signingSecret: string;
|
|
23
23
|
version: string;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Lifecycle lookup for presented API keys — the strict, recommended hook from
|
|
26
|
+
* `@hasna/contracts/auth` (`ApiKeyStore.keyStatus`). Anything other than
|
|
27
|
+
* "active" denies. Required: the contracts verifier fails closed at
|
|
28
|
+
* construction without it, so a service cannot boot with no revocation check.
|
|
29
|
+
*/
|
|
30
|
+
keyStatus: KeyStatusResolver;
|
|
27
31
|
audit?: (e: AuthAuditEvent) => void;
|
|
28
32
|
}
|
|
29
33
|
export interface ServeApp {
|
package/dist/server/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/server/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/server/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAiC1E,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,gBAAgB,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,SAAS,EAAE,iBAAiB,CAAC;IAC7B,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACzC;AAeD,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,QAAQ,CA8YjE"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
* domains-serve — standalone HTTP API server
|
|
3
|
+
* domains-serve — standalone HTTP API server, PostgreSQL backend.
|
|
4
4
|
*
|
|
5
5
|
* Usage: domains-serve [--port 8080] [--host 0.0.0.0]
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* HASNA_DOMAINS_STORAGE_MODE "cloud"
|
|
7
|
+
* The server backend is selected by the environment:
|
|
8
|
+
* HASNA_DOMAINS_DATABASE_URL Postgres DSN -> PostgreSQL backend
|
|
9
|
+
* (unset -> SQLite backend)
|
|
11
10
|
* HASNA_DOMAINS_API_SIGNING_KEY HMAC signing secret for API keys
|
|
12
11
|
* Falls back to the generic DATABASE_URL / API_KEY_SIGNING_SECRET env names the
|
|
13
12
|
* hasna-app Terraform module injects.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AASH,eAAO,MAAM,gBAAgB,+FAInB,CAAC;AAEX,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,IAAI,CAIvE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAQjF"}
|