insta 0.1.3 → 0.1.4

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.
@@ -336,4 +336,91 @@ export async function domainDetach(host, opts, deps) {
336
336
  }
337
337
  return removeDomain(name, opts, d);
338
338
  }
339
+ const zonePath = (orgId, domainName) => `/orgs/${encodeURIComponent(orgId)}/zones${domainName ? `/${encodeURIComponent(domainName.trim().toLowerCase())}` : ''}`;
340
+ export function zoneLines(z, orgFlag) {
341
+ const orgArg = orgFlag ? ` --org ${orgFlag}` : '';
342
+ if (z.status === 'active')
343
+ return [`${z.domainName} delegated (${z.nameservers.join(', ')})`];
344
+ return [
345
+ `${z.domainName} waiting for nameservers`,
346
+ ` set these at your domain's registrar: ${z.nameservers.join(', ')}`,
347
+ ` review the zone BEFORE switching: insta domain zone records ${z.domainName}${orgArg}`,
348
+ ];
349
+ }
350
+ export function zoneRecordLines(records) {
351
+ if (!records.length)
352
+ return ['no records in the zone yet — the provider scan runs shortly after delegating; run this again in a moment'];
353
+ const w = (pick) => Math.max(...records.map((r) => pick(r).length));
354
+ const typeW = w((r) => r.type), hostW = w((r) => r.host), answerW = w((r) => r.answer);
355
+ return records.map((r) => ` ${r.type.padEnd(typeW)} ${r.host.padEnd(hostW)} ${r.answer.padEnd(answerW)}${r.ttl !== undefined ? ` ttl ${r.ttl}` : ''}${r.priority !== undefined ? ` prio ${r.priority}` : ''}${r.proxied ? ' (proxied)' : ''}`);
356
+ }
357
+ /**
358
+ * Delegate a bring-your-own domain: the platform builds a managed zone for it and answers the two
359
+ * nameservers to set at the domain's own registrar. From then on every attach publishes its records
360
+ * into the zone itself — apexes included — instead of printing them for hand-copying. The zone is
361
+ * SEEDED by the provider's record scan, which is a heuristic: the review-then-switch contract
362
+ * (printed, and enforced by nothing else) is to compare `zone records` against the domain's current
363
+ * DNS and add what is missing at the CURRENT provider — re-running delegate re-imports — before
364
+ * re-pointing. A domain carrying live MX records is refused outright: a DNS move that can drop
365
+ * mail is never done implicitly. 202 approval_required in agent mode (zone.delegate); org admin
366
+ * either way.
367
+ */
368
+ const orgArgOf = (opts) => (opts.org ? ` --org ${opts.org}` : '');
369
+ export async function zoneDelegate(domainName, opts, deps) {
370
+ const { api, orgId } = await orgDeps(opts, deps);
371
+ // Same precedent as `domain delegate`: the org route signs for the linked project in agent mode
372
+ // (zone.delegate is read at the session project), but only when the link belongs to the org
373
+ // being mutated — under `--org` naming another org the call goes projectless and the platform's
374
+ // org-administration path judges it.
375
+ const link = deps?.project ?? (await readProject()) ?? undefined;
376
+ const projectId = link && link.orgId === orgId ? link.projectId : undefined;
377
+ const res = await api.rawRequest('POST', zonePath(orgId), { domainName }, projectId ? { projectId } : undefined);
378
+ if (handleApproval(res, opts.json))
379
+ return;
380
+ if (opts.json)
381
+ return printJson(res.body);
382
+ const z = res.body;
383
+ for (const line of zoneLines(z, opts.org))
384
+ info(line);
385
+ info(`the zone was seeded by a provider scan — a heuristic. Check \`insta domain zone records ${z.domainName}${orgArgOf(opts)}\` against your current DNS, add anything missing at your CURRENT provider (re-running delegate re-imports), and only then switch the nameservers.`);
386
+ }
387
+ export async function zoneList(opts, deps) {
388
+ const { api, orgId } = await orgDeps(opts, deps);
389
+ const r = await api.request('GET', zonePath(orgId));
390
+ if (opts.json)
391
+ return printJson(r);
392
+ if (!r.items.length)
393
+ return info('no delegated zones — start one: insta domain zone delegate <domain>');
394
+ for (const z of r.items)
395
+ for (const line of zoneLines(z, opts.org))
396
+ info(line);
397
+ }
398
+ export async function zoneRecords(domainName, opts, deps) {
399
+ const { api, orgId } = await orgDeps(opts, deps);
400
+ const r = await api.request('GET', `${zonePath(orgId, domainName)}/records`);
401
+ if (opts.json)
402
+ return printJson(r);
403
+ for (const line of zoneRecordLines(r.items))
404
+ info(line);
405
+ if (r.items.length)
406
+ info(`every type shows here (the scan is a heuristic) — add anything missing at your current DNS provider and re-run \`insta domain zone delegate ${domainName.trim().toLowerCase()}${orgArgOf(opts)}\` to re-import before switching nameservers`);
407
+ }
408
+ /**
409
+ * Release a delegated zone: the platform prunes the records it published and deletes the managed
410
+ * zone. The customer's next step — printed — is pointing the domain's nameservers back at their
411
+ * own provider; hostnames then re-verify on the records path.
412
+ */
413
+ export async function zoneRelease(domainName, opts, deps) {
414
+ const { api, orgId } = await orgDeps(opts, deps);
415
+ const link = deps?.project ?? (await readProject()) ?? undefined;
416
+ const projectId = link && link.orgId === orgId ? link.projectId : undefined;
417
+ const res = await api.rawRequest('DELETE', zonePath(orgId, domainName), undefined, projectId ? { projectId } : undefined);
418
+ if (handleApproval(res, opts.json))
419
+ return;
420
+ if (opts.json)
421
+ return printJson(res.body);
422
+ const r = res.body;
423
+ info(`${r.domainName} released`);
424
+ info(`point the domain's nameservers back at your DNS provider — hostnames re-verify on the records path (insta domain attach prints them)`);
425
+ }
339
426
  //# sourceMappingURL=domain.js.map
package/dist/index.js CHANGED
@@ -279,6 +279,19 @@ ns.command('set <domain> <nameservers...>').description("Delegate the zone to na
279
279
  ns.command('reset <domain>').description("Put the zone back on the registrar's own nameservers — from your own delegation or from an InstaCloud-managed zone alike; a hostname `set` took down is re-attached with `insta domain attach`, one a managed zone was serving re-verifies on its own")
280
280
  .option('--org <id>', "target org (default: linked project's org)").option('--json')
281
281
  .action(guard((domain, o) => domainCmd.domainNameserversReset(domain, o)));
282
+ const zone = dom.command('zone').description("Bring-your-own domains served from an InstaCloud-managed zone (nameserver delegation): point the domain's registrar at the pair `zone delegate` answers, and every attach's records — apexes included — are published for you. The alternative stays available: `insta domain attach` alone prints records to paste into your own zone");
283
+ zone.command('delegate <domain>').description("Delegate a domain you own elsewhere: builds its managed zone (seeded by a provider record scan — a HEURISTIC, so review `zone records` and add anything missing at your current provider BEFORE switching nameservers; re-running delegate re-imports) and answers the two nameservers to set at your registrar. A domain carrying live MX records is refused — move mail first or keep the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")
284
+ .option('--org <id>', "target org (default: linked project's org)").option('--json')
285
+ .action(guard((domain, o) => domainCmd.zoneDelegate(domain, o)));
286
+ zone.command('list').description("The org's delegated zones: which are still waiting for the nameserver switch and which are serving")
287
+ .option('--org <id>', "target org (default: linked project's org)").option('--json')
288
+ .action(guard((o) => domainCmd.zoneList(o)));
289
+ zone.command('records <domain>').description("Every record in the delegated zone — the pre-switch review. The scan seeds common records but is not exhaustive: compare against your current DNS and add what is missing at your CURRENT provider, then re-run `zone delegate` to re-import")
290
+ .option('--org <id>', "target org (default: linked project's org)").option('--json')
291
+ .action(guard((domain, o) => domainCmd.zoneRecords(domain, o)));
292
+ zone.command('release <domain>').description("Release a delegated zone: the platform's records are pruned and the zone deleted. Point the nameservers back at your own provider; hostnames re-verify on the records path (org admin; gated: zone.delegate — agent mode gates from a linked project, an unlinked --org call falls under org administration instead)")
293
+ .option('--org <id>', "target org (default: linked project's org)").option('--json')
294
+ .action(guard((domain, o) => domainCmd.zoneRelease(domain, o)));
282
295
  const xfer = dom.command('transfer').description('Take a bought domain to another registrar — open the lock, then read the code (all three need org admin)');
283
296
  xfer.command('lock <domain> <mode>').description("Open or close the registrar transfer lock (mode: on|off). ICANN's own 60-day lock on a new registration outranks it")
284
297
  .option('--org <id>', "target org (default: linked project's org)").option('--json')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insta",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "InstaCloud CLI — a thin client of the platform control-plane API.",
6
6
  "keywords": [