ninepm 0.83.0 → 0.87.0

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 CHANGED
@@ -269,9 +269,35 @@ another service's certificate check uses `_acme-challenge`) is not ours, so leav
269
269
  that sends no such list it prints no deletion advice at all. When the removal is still finishing
270
270
  upstream (the API answers `202` with `releasing: true`), it says so, names no record to delete, and
271
271
  prints the `9pm domains list` command: once the domain is no longer listed, it has stopped serving
272
- from 9pm. A removal that finishes in the background returns no record list, so the CLI cannot say
273
- which DNS records to delete for it. Such a domain shows as `removing`
274
- in `list` and `status` (`"removing": true` in `--json`), and `wait` on it exits `20`. Since 0.80.0.
272
+ from 9pm. Such a domain shows as `removing` in `list` and `status` (`"removing": true` in `--json`),
273
+ and `wait` on it exits `20`. Since 0.80.0.
274
+
275
+ Since 0.86.0, a removal that finished (from `remove` or `9pm delete`) also says that after removal,
276
+ visitors may see a TLS or routing error while the domain's DNS still points at 9pm. It names no
277
+ record and adds no deletion advice of its own: what may be deleted is only the cleanup list printed
278
+ above it, each record only if its value still matches. A removal still finishing gets no such line,
279
+ since it is told to leave its records in place. A server that sends no cleanup list still gets it.
280
+
281
+ Since 0.87.0, that line is followed by the order the staging rehearsal (#2182) demonstrated for a
282
+ move to another host: point DNS at the new host, wait for the DNS TTL to pass, then remove the domain
283
+ from 9pm. While caches update, some visitors may still reach 9pm or briefly see an error from the new
284
+ host, and once DNS points away 9pm may show the domain as failed, which is expected. It links the
285
+ custom-domain guide's "Moving a domain off 9pm" section on the site beside the control plane it
286
+ talked to (staging's guide for `--api-url https://api-staging.9pm.ai`). A move to your own Cloudflare
287
+ DNS zone was not rehearsed, so no order is printed for it; the guide says to contact support first.
288
+
289
+ Since 0.84.0, `9pm domains removed [--owner <organisation>]` lists the DNS records you may delete
290
+ after a removal that finished in the background: a `remove` that answered `releasing`, or a domain of
291
+ a deleted app, finished later by the platform. It is the same list `remove` prints when it finishes
292
+ itself, under the same rule (delete a record only while it still has the value shown), and the
293
+ printed hint after a still-finishing removal or `9pm delete` points at it, with `--owner` and
294
+ `--api-url` kept. A removal is listed for 30 days after it finishes, only to the account that owned
295
+ the domain, and not at all once the hostname has been added again anywhere: the server checks the
296
+ certificate provider for it when you run the command, and a domain it couldn't check is named without
297
+ its records, with the command to ask about it again. The server checks ten domains per page; the
298
+ command reads up to 20 pages and says so if more remain. `--hostname <hostname>` asks about one
299
+ domain only. `--json` prints the API's answer with its pages merged (`next` is null once every page
300
+ was read). Against a server without the route it says it can't list any, prints no record and exits `1`.
275
301
 
276
302
  Since 0.83.0, `9pm domains cleanup <app> <hostname>` lists the setup TXT records 9pm issued for a
277
303
  live domain and no longer needs, such as an earlier `_acme-challenge` value or the
@@ -332,6 +358,7 @@ and none can be listed for it later.
332
358
  9pm domains restart <app> <hostname> # only after a certificate failed; record values may change
333
359
  9pm domains remove <app> <hostname> --confirm <hostname> [--json]
334
360
  9pm domains move [<app>] <hostname> --to <app> [--json]
361
+ 9pm domains removed [--hostname <hostname>] [--owner <organisation>] [--json] # records to delete after a removal that finished in the background
335
362
  9pm access show <site>
336
363
  9pm access public <site> [--switch-mode]
337
364
  9pm access otp <site> [--switch-mode]
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Agent } from "undici";
3
+ import { type ProjectQrAddress } from "./qr-code.js";
3
4
  import { type DomainNext, type DomainReason, type DomainRecordStatus, type DomainState, type DomainStateDnsCheck, type DomainStateDnsInstruction, type DomainStateInput } from "./domain-state.js";
4
5
  import type { AccessGrant, AccessGrantType, AccessProviderKey, AppAccessSettings, AppAccessServiceToken, AppAccessToken, AppDatabase, AppRuntime, AppUserFilesUsage, ContainerImageUploadInstruction, ContainerRegion, Deployment, ManagedFileObject, ManagedFileShare, ManagedFileUsage, Project } from "./cli-types.js";
5
6
  export interface DeployOptions {
@@ -122,7 +123,7 @@ export interface DeletedSiteResult {
122
123
  metadata: boolean;
123
124
  errors?: string[];
124
125
  }
125
- export declare function deletedDomainLines(deleted: Pick<DeletedSiteResult, "customDomainCleanup" | "customDomainsReleasing">): string[];
126
+ export declare function deletedDomainLines(deleted: Pick<DeletedSiteResult, "customDomainCleanup" | "customDomainsReleasing" | "customDomainNames">, options?: DomainFormatOptions): string[];
126
127
  export interface CreatedRuntimeToken {
127
128
  token: string;
128
129
  appAccessToken: AppAccessToken;
@@ -132,7 +133,7 @@ export interface CreatedServiceToken {
132
133
  clientSecret: string;
133
134
  }
134
135
  export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
135
- export declare const CLI_VERSION = "0.83.0";
136
+ export declare const CLI_VERSION = "0.87.0";
136
137
  type CredentialBackend = "macos-keychain" | "windows-dpapi" | "linux-secret-service" | "file-plaintext" | "none";
137
138
  export interface StoredCredentialEntry {
138
139
  token: string;
@@ -532,6 +533,7 @@ type CliCommand = {
532
533
  confirmSlug: string;
533
534
  apiUrl: string;
534
535
  token: string;
536
+ apiUrlFrom?: ApiUrlSource;
535
537
  } | {
536
538
  kind: "apps";
537
539
  apiUrl: string;
@@ -628,6 +630,17 @@ type CliCommand = {
628
630
  };
629
631
  apiUrl: string;
630
632
  token: string;
633
+ } | {
634
+ kind: "qr";
635
+ site: string;
636
+ owner?: string;
637
+ address?: string;
638
+ format: "svg" | "png";
639
+ out?: string;
640
+ terminal: boolean;
641
+ json: boolean;
642
+ apiUrl: string;
643
+ token: string;
631
644
  } | {
632
645
  kind: "domains";
633
646
  owner?: string;
@@ -668,6 +681,10 @@ type CliCommand = {
668
681
  hostname: string;
669
682
  to: string;
670
683
  json: boolean;
684
+ } | {
685
+ kind: "removed";
686
+ json: boolean;
687
+ hostname?: string;
671
688
  };
672
689
  apiUrl: string;
673
690
  token: string;
@@ -880,7 +897,21 @@ export declare function getDomainSetupCleanup(options: DomainCommandOptions & {
880
897
  }): Promise<unknown>;
881
898
  export declare function domainSetupCleanupLines(body: unknown, hostname: string): string[];
882
899
  export declare function setupCleanupLines(hostname: string, records: DomainCleanupRecord[]): string[];
900
+ export declare function customDomainMoveOffGuideUrl(options?: DomainFormatOptions): string;
901
+ export declare function domainRemovedConsequenceLines(hostnames: string[], options?: DomainFormatOptions): string[];
883
902
  export declare function domainRemovalPendingLines(hostname: string, app: string, options: DomainFormatOptions): string[];
903
+ export declare const FINISHED_REMOVAL_PAGES_PER_COMMAND = 20;
904
+ export declare function getFinishedDomainRemovals(options: ApiOptions & {
905
+ owner?: string;
906
+ hostname?: string;
907
+ }): Promise<unknown>;
908
+ export interface FinishedDomainRemoval {
909
+ hostname: string;
910
+ finishedAt: string | null;
911
+ cleanup: DomainCleanup;
912
+ }
913
+ export declare function parseFinishedDomainRemovals(body: unknown): FinishedDomainRemoval[] | null;
914
+ export declare function finishedDomainRemovalLines(body: unknown, options?: DomainFormatOptions): string[];
884
915
  export interface DomainMove {
885
916
  body: unknown;
886
917
  hostname: string;
@@ -923,6 +954,23 @@ export declare function waitForDomain(options: DomainCommandOptions & {
923
954
  onRetry?: (error: unknown) => void;
924
955
  }): Promise<DomainWaitResult>;
925
956
  export declare function healthWithReason(status: string | null | undefined, reason: string | null | undefined): string;
957
+ export declare const QR_PNG_PIXELS_PER_MODULE = 16;
958
+ /**
959
+ * The minimum width in centimetres, rounded UP to the millimetre: it is a floor, so rounding
960
+ * to nearest would print a 26.5 mm minimum as "2.6 cm", below what the code needs.
961
+ */
962
+ export declare function qrMinimumWidthCmText(minimumWidthMm: number): string;
963
+ export declare const QR_ACCESS_NOTE = "This app is private. Scanning the code doesn't grant access: people still have to sign in and be allowed in.";
964
+ export declare function qrCustomDomainNote(hostname: string): string;
965
+ /**
966
+ * The returned address `--address` names, or an error listing the valid ones. The value is
967
+ * normalized first - a bare hostname gets `https://`, case is ignored, a trailing `/` is
968
+ * allowed - and must then match an address the server offered: no other host, path, query,
969
+ * fragment, credentials, port or scheme. The CLI never encodes a URL the server did not offer.
970
+ */
971
+ export declare function pickQrAddress(addresses: readonly ProjectQrAddress[], requested: string | undefined): ProjectQrAddress;
972
+ /** An 8-bit greyscale PNG of a module matrix (quiet zone included), black on white. */
973
+ export declare function encodeQrPng(modules: readonly (readonly boolean[])[], scale?: number): Buffer;
926
974
  export type ApiUrlSource = "flag" | "env" | "default";
927
975
  export declare function workerPublicFiles(paths: readonly string[], serverEntry: string, selection: string | null): Array<{
928
976
  path: string;