@atlasauth/cli 0.1.0 → 0.3.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/CHANGELOG.md +38 -0
- package/README.md +74 -1
- package/dist/args.d.ts +82 -1
- package/dist/args.js +40 -0
- package/dist/args.js.map +1 -1
- package/dist/commands/actions.d.ts +11 -0
- package/dist/commands/actions.js +120 -0
- package/dist/commands/actions.js.map +1 -0
- package/dist/commands/api-keys.js +18 -3
- package/dist/commands/api-keys.js.map +1 -1
- package/dist/commands/auth.js +7 -3
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.js +721 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +409 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/project-commands.d.ts +8 -0
- package/dist/commands/project-commands.js +236 -0
- package/dist/commands/project-commands.js.map +1 -0
- package/dist/commands/webhooks.d.ts +5 -2
- package/dist/commands/webhooks.js +37 -3
- package/dist/commands/webhooks.js.map +1 -1
- package/dist/config-http.d.ts +27 -0
- package/dist/config-http.js +63 -0
- package/dist/config-http.js.map +1 -0
- package/dist/context.d.ts +17 -1
- package/dist/context.js +4 -0
- package/dist/context.js.map +1 -1
- package/dist/help.d.ts +1 -1
- package/dist/help.js +104 -4
- package/dist/help.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/dist/project.d.ts +63 -0
- package/dist/project.js +86 -0
- package/dist/project.js.map +1 -0
- package/dist/run.js +10 -0
- package/dist/run.js.map +1 -1
- package/dist/secrets.d.ts +41 -0
- package/dist/secrets.js +142 -0
- package/dist/secrets.js.map +1 -0
- package/dist/serialize.d.ts +31 -0
- package/dist/serialize.js +248 -0
- package/dist/serialize.js.map +1 -0
- package/package.json +5 -3
package/dist/help.js
CHANGED
|
@@ -17,8 +17,14 @@ Commands:
|
|
|
17
17
|
users list|get|create|delete
|
|
18
18
|
orgs list|get|members
|
|
19
19
|
api-keys list|create|revoke
|
|
20
|
-
webhooks list
|
|
20
|
+
webhooks list|create
|
|
21
21
|
sessions revoke
|
|
22
|
+
config export|import Config-as-code: round-trip the instance's config
|
|
23
|
+
actions list|deploy|test|logs
|
|
24
|
+
init Create or complete an app's setup (idempotent); secrets to 0600 files
|
|
25
|
+
preflight Check an instance's setup; non-zero exit on errors (for CI)
|
|
26
|
+
env Print deploy variables: public values + secret file paths
|
|
27
|
+
client rotate Rotate an OAuth client secret straight into its file
|
|
22
28
|
|
|
23
29
|
Global options:
|
|
24
30
|
--profile <name> Use a named profile (default: active, else "default")
|
|
@@ -31,6 +37,10 @@ Global options:
|
|
|
31
37
|
Environment overrides:
|
|
32
38
|
ATLAS_SECRET_KEY Secret key to use instead of the stored one
|
|
33
39
|
ATLAS_API_URL API URL to use instead of the stored one
|
|
40
|
+
ATLAS_PLATFORM_KEY Platform key (psk_) for atlas init
|
|
41
|
+
|
|
42
|
+
Secrets: a command that receives a secret writes it to a 0600 file under
|
|
43
|
+
~/.config/<product>/ and prints only the path. --print-secret prints it instead.
|
|
34
44
|
|
|
35
45
|
Run 'atlas <command> --help' for command-specific help.`;
|
|
36
46
|
/** Per-command help, keyed by the resource word. */
|
|
@@ -65,15 +75,105 @@ Usage:
|
|
|
65
75
|
|
|
66
76
|
Usage:
|
|
67
77
|
atlas api-keys list [--subject-type <user|organization>] [--subject-id <id>] [--json]
|
|
68
|
-
atlas api-keys create --subject-type <user|organization> --subject-id <id> [--name <n>]
|
|
78
|
+
atlas api-keys create --subject-type <user|organization> --subject-id <id> [--name <n>]
|
|
79
|
+
[--product <name>] [--force] [--print-secret] [--json]
|
|
69
80
|
atlas api-keys revoke <api-key-id> [--json]`,
|
|
70
|
-
webhooks: `atlas webhooks —
|
|
81
|
+
webhooks: `atlas webhooks — manage webhook endpoints
|
|
82
|
+
|
|
83
|
+
Usage:
|
|
84
|
+
atlas webhooks list [--json]
|
|
85
|
+
atlas webhooks create --url <https-url> [--events a,b] [--product <name>] [--print-secret] [--json]
|
|
86
|
+
|
|
87
|
+
create writes the signing secret to a 0600 file and prints only its path.`,
|
|
88
|
+
init: `atlas init — create or complete an app's setup, idempotently
|
|
71
89
|
|
|
72
90
|
Usage:
|
|
73
|
-
atlas
|
|
91
|
+
atlas init --product <name> [--domain <domain>] [--auth-host <host>]
|
|
92
|
+
[--instance-env development|production] [--name <app-name>]
|
|
93
|
+
[--strategies password,email_code,...]
|
|
94
|
+
[--email managed|development|none] [--email-from <address>]
|
|
95
|
+
[--email-provider resend|brevo --email-key-file <path|->]
|
|
96
|
+
[--client-redirect <url>]... [--webhook-url <url>]
|
|
97
|
+
[--yes] [--force] [--rotate-secret] [--json]
|
|
98
|
+
|
|
99
|
+
Needs a platform key (ATLAS_PLATFORM_KEY, or a psk_ profile from atlas login).
|
|
100
|
+
Finds the app by name and environment, or creates it, then sets strategies, the
|
|
101
|
+
email sender, the custom domain (auth.<domain> unless --auth-host; prints the
|
|
102
|
+
CNAME), a first-party OAuth client and a webhook. Re-running only fills what is
|
|
103
|
+
missing.
|
|
104
|
+
|
|
105
|
+
Email always goes through YOUR provider account (Atlas lends none):
|
|
106
|
+
managed Atlas sets your domain up on your Resend or Brevo account and
|
|
107
|
+
prints the DNS records.
|
|
108
|
+
development development instances only: your provider without a verified
|
|
109
|
+
domain (Resend's onboarding@resend.dev by default), capped.
|
|
110
|
+
The key is read from --email-key-file (a file holding the key or KEY=value; '-'
|
|
111
|
+
reads stdin). It is never taken from argv and never printed.
|
|
112
|
+
|
|
113
|
+
Every secret goes to ~/.config/<product>/<env>*.env (dir 0700, files 0600) and
|
|
114
|
+
is never printed. An existing file is never overwritten without --force.
|
|
115
|
+
--rotate-secret mints a new secret key when the app exists but its file is gone.
|
|
116
|
+
Public values and file paths are kept in ~/.config/<product>/atlas.json.`,
|
|
117
|
+
preflight: `atlas preflight — check an instance's setup (a CI / deploy gate)
|
|
118
|
+
|
|
119
|
+
Usage: atlas preflight [--instance <id> | --product <name> [--instance-env <env>]]
|
|
120
|
+
[--fail-on error|warning] [--json]
|
|
121
|
+
|
|
122
|
+
Prints every finding (severity, code, message, fix). Exits 1 when there is an
|
|
123
|
+
error, or with --fail-on warning also on a warning.`,
|
|
124
|
+
env: `atlas env — print deploy variables without secrets
|
|
125
|
+
|
|
126
|
+
Usage: atlas env --product <name> [--instance-env <env>] [--format dotenv|coolify|vercel] [--json]
|
|
127
|
+
|
|
128
|
+
Public values (publishable key, Frontend API, issuer, discovery and JWKS URLs,
|
|
129
|
+
OAuth client id) are printed as-is. Secrets are printed as references to their
|
|
130
|
+
0600 files: *_FILE=<path> (dotenv), KEY=@<path> (coolify), or a vercel env add
|
|
131
|
+
command that pipes the value from the file (vercel).`,
|
|
132
|
+
client: `atlas client — OAuth clients
|
|
133
|
+
|
|
134
|
+
Usage: atlas client rotate <client-id> [--now | --overlap <seconds>]
|
|
135
|
+
[--instance <id> | --product <name> [--instance-env <env>]]
|
|
136
|
+
[--print-secret]
|
|
137
|
+
|
|
138
|
+
Rotates the client secret and writes the new one straight to its 0600 file.
|
|
139
|
+
--now revokes the old secret immediately; --overlap keeps it working for that
|
|
140
|
+
many seconds. Prints when the old secret stops working.`,
|
|
74
141
|
sessions: `atlas sessions — manage sessions
|
|
75
142
|
|
|
76
143
|
Usage:
|
|
77
144
|
atlas sessions revoke <session-id> [--json]`,
|
|
145
|
+
config: `atlas config — config-as-code for an Atlas instance (the Auth0 deploy-cli peer)
|
|
146
|
+
|
|
147
|
+
Usage:
|
|
148
|
+
atlas config export [--dir <path>] [--format json|yaml]
|
|
149
|
+
atlas config import [--dir <path>] [--format json|yaml] [--dry-run] [--prune]
|
|
150
|
+
|
|
151
|
+
export Read the instance's current config (instance/auth config, OAuth clients
|
|
152
|
+
and providers, SSO connections, resource servers, JWT templates, roles,
|
|
153
|
+
webhook endpoints, billing plans, redirect URLs, org settings schema,
|
|
154
|
+
notification and email templates, org policies) and write a deterministic
|
|
155
|
+
directory tree — one file per resource type, keys sorted, SECRETS OMITTED.
|
|
156
|
+
|
|
157
|
+
import Diff the directory against live config and apply creates/updates
|
|
158
|
+
idempotently. A resource type with no file is left untouched.
|
|
159
|
+
--dry-run print the plan and apply nothing
|
|
160
|
+
--prune also delete live resources absent from the config
|
|
161
|
+
--dir config directory (default: ./atlas-config)
|
|
162
|
+
--format json (default) or yaml
|
|
163
|
+
|
|
164
|
+
Secrets never live on disk. Inject a credential at import with a \${ENV_VAR}
|
|
165
|
+
placeholder in a file (e.g. a provider "client_secret": "\${GOOGLE_SECRET}").`,
|
|
166
|
+
actions: `atlas actions — manage Actions (tenant code run in the auth pipeline)
|
|
167
|
+
|
|
168
|
+
Usage:
|
|
169
|
+
atlas actions list [--json]
|
|
170
|
+
atlas actions deploy <file> --trigger <post_login|pre_user_registration|post_user_registration> [--name <n>] [--json]
|
|
171
|
+
atlas actions test <file> [--trigger <t>] [--json]
|
|
172
|
+
atlas actions logs <action-id> [--json]
|
|
173
|
+
|
|
174
|
+
deploy uploads the file's code verbatim as a new action (created, not yet bound).
|
|
175
|
+
test runs the file OFFLINE through the @atlasauth/actions harness — no network,
|
|
176
|
+
no real users — and exits non-zero if the action throws. logs explains where an
|
|
177
|
+
action's execution history is recorded (Atlas has no live log-stream endpoint).`,
|
|
78
178
|
};
|
|
79
179
|
//# sourceMappingURL=help.js.map
|
package/dist/help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEU,QAAA,QAAQ,GAAG
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEU,QAAA,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wDAoCgC,CAAC;AAEzD,oDAAoD;AACvC,QAAA,YAAY,GAA2B;IAClD,KAAK,EAAE;;;;;;mCAM0B;IAEjC,MAAM,EAAE;;uCAE6B;IAErC,MAAM,EAAE;;gDAEsC;IAE9C,KAAK,EAAE;;;;;;wCAM+B;IAEtC,IAAI,EAAE;;;;;uCAK+B;IAErC,UAAU,EAAE;;;;;;8CAMgC;IAE5C,QAAQ,EAAE;;;;;;0EAM8D;IAExE,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;yEA4BiE;IAEvE,SAAS,EAAE;;;;;;oDAMuC;IAElD,GAAG,EAAE;;;;;;;qDAO8C;IAEnD,MAAM,EAAE;;;;;;;;wDAQ8C;IAEtD,QAAQ,EAAE;;;8CAGkC;IAE5C,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;8EAoBoE;IAC5E,OAAO,EAAE;;;;;;;;;;;gFAWqE;CAC/E,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { run, type Handler } from './run';
|
|
8
8
|
export { defaultContext, type RunContext, type ManagementClient } from './context';
|
|
9
|
+
export { createConfigHttp, isNotFound, type ConfigHttp } from './config-http';
|
|
9
10
|
export { CliError } from './errors';
|
|
10
11
|
export { defaultIO, captureIO, type IO, type CaptureIO } from './io';
|
|
11
12
|
export { FileConfigStore, MemoryConfigStore, defaultConfigPath, type ConfigStore, type ConfigFile, type Profile, } from './config';
|
|
@@ -17,3 +18,9 @@ export { orgs } from './commands/orgs';
|
|
|
17
18
|
export { apiKeys } from './commands/api-keys';
|
|
18
19
|
export { webhooks } from './commands/webhooks';
|
|
19
20
|
export { sessions } from './commands/sessions';
|
|
21
|
+
export { config } from './commands/config';
|
|
22
|
+
export { actions } from './commands/actions';
|
|
23
|
+
export { init } from './commands/init';
|
|
24
|
+
export { preflight, envCommand, client } from './commands/project-commands';
|
|
25
|
+
export { writeSecretFile, readEnvFile, parseDotenv, renderDotenv, productDir, deliverSecret, } from './secrets';
|
|
26
|
+
export { readManifest, writeManifest, manifestPath, type ProjectManifest, type ProjectInstance, type InstanceEnv, } from './project';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sessions = exports.webhooks = exports.apiKeys = exports.orgs = exports.users = exports.whoami = exports.logout = exports.login = exports.getVersion = exports.defaultPrompter = exports.defaultConfigPath = exports.MemoryConfigStore = exports.FileConfigStore = exports.captureIO = exports.defaultIO = exports.CliError = exports.defaultContext = exports.run = void 0;
|
|
3
|
+
exports.manifestPath = exports.writeManifest = exports.readManifest = exports.deliverSecret = exports.productDir = exports.renderDotenv = exports.parseDotenv = exports.readEnvFile = exports.writeSecretFile = exports.client = exports.envCommand = exports.preflight = exports.init = exports.actions = exports.config = exports.sessions = exports.webhooks = exports.apiKeys = exports.orgs = exports.users = exports.whoami = exports.logout = exports.login = exports.getVersion = exports.defaultPrompter = exports.defaultConfigPath = exports.MemoryConfigStore = exports.FileConfigStore = exports.captureIO = exports.defaultIO = exports.CliError = exports.isNotFound = exports.createConfigHttp = exports.defaultContext = exports.run = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Library surface of `@atlasauth/cli`. The package ships a bin (`atlas`), but
|
|
6
6
|
* exporting the pieces lets the handlers be unit-tested with injected deps and
|
|
@@ -11,6 +11,9 @@ var run_1 = require("./run");
|
|
|
11
11
|
Object.defineProperty(exports, "run", { enumerable: true, get: function () { return run_1.run; } });
|
|
12
12
|
var context_1 = require("./context");
|
|
13
13
|
Object.defineProperty(exports, "defaultContext", { enumerable: true, get: function () { return context_1.defaultContext; } });
|
|
14
|
+
var config_http_1 = require("./config-http");
|
|
15
|
+
Object.defineProperty(exports, "createConfigHttp", { enumerable: true, get: function () { return config_http_1.createConfigHttp; } });
|
|
16
|
+
Object.defineProperty(exports, "isNotFound", { enumerable: true, get: function () { return config_http_1.isNotFound; } });
|
|
14
17
|
var errors_1 = require("./errors");
|
|
15
18
|
Object.defineProperty(exports, "CliError", { enumerable: true, get: function () { return errors_1.CliError; } });
|
|
16
19
|
var io_1 = require("./io");
|
|
@@ -39,4 +42,25 @@ var webhooks_1 = require("./commands/webhooks");
|
|
|
39
42
|
Object.defineProperty(exports, "webhooks", { enumerable: true, get: function () { return webhooks_1.webhooks; } });
|
|
40
43
|
var sessions_1 = require("./commands/sessions");
|
|
41
44
|
Object.defineProperty(exports, "sessions", { enumerable: true, get: function () { return sessions_1.sessions; } });
|
|
45
|
+
var config_2 = require("./commands/config");
|
|
46
|
+
Object.defineProperty(exports, "config", { enumerable: true, get: function () { return config_2.config; } });
|
|
47
|
+
var actions_1 = require("./commands/actions");
|
|
48
|
+
Object.defineProperty(exports, "actions", { enumerable: true, get: function () { return actions_1.actions; } });
|
|
49
|
+
var init_1 = require("./commands/init");
|
|
50
|
+
Object.defineProperty(exports, "init", { enumerable: true, get: function () { return init_1.init; } });
|
|
51
|
+
var project_commands_1 = require("./commands/project-commands");
|
|
52
|
+
Object.defineProperty(exports, "preflight", { enumerable: true, get: function () { return project_commands_1.preflight; } });
|
|
53
|
+
Object.defineProperty(exports, "envCommand", { enumerable: true, get: function () { return project_commands_1.envCommand; } });
|
|
54
|
+
Object.defineProperty(exports, "client", { enumerable: true, get: function () { return project_commands_1.client; } });
|
|
55
|
+
var secrets_1 = require("./secrets");
|
|
56
|
+
Object.defineProperty(exports, "writeSecretFile", { enumerable: true, get: function () { return secrets_1.writeSecretFile; } });
|
|
57
|
+
Object.defineProperty(exports, "readEnvFile", { enumerable: true, get: function () { return secrets_1.readEnvFile; } });
|
|
58
|
+
Object.defineProperty(exports, "parseDotenv", { enumerable: true, get: function () { return secrets_1.parseDotenv; } });
|
|
59
|
+
Object.defineProperty(exports, "renderDotenv", { enumerable: true, get: function () { return secrets_1.renderDotenv; } });
|
|
60
|
+
Object.defineProperty(exports, "productDir", { enumerable: true, get: function () { return secrets_1.productDir; } });
|
|
61
|
+
Object.defineProperty(exports, "deliverSecret", { enumerable: true, get: function () { return secrets_1.deliverSecret; } });
|
|
62
|
+
var project_1 = require("./project");
|
|
63
|
+
Object.defineProperty(exports, "readManifest", { enumerable: true, get: function () { return project_1.readManifest; } });
|
|
64
|
+
Object.defineProperty(exports, "writeManifest", { enumerable: true, get: function () { return project_1.writeManifest; } });
|
|
65
|
+
Object.defineProperty(exports, "manifestPath", { enumerable: true, get: function () { return project_1.manifestPath; } });
|
|
42
66
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,6BAA0C;AAAjC,0FAAA,GAAG,OAAA;AACZ,qCAAmF;AAA1E,yGAAA,cAAc,OAAA;AACvB,mCAAoC;AAA3B,kGAAA,QAAQ,OAAA;AACjB,2BAAqE;AAA5D,+FAAA,SAAS,OAAA;AAAE,+FAAA,SAAS,OAAA;AAC7B,mCAOkB;AANhB,yGAAA,eAAe,OAAA;AACf,2GAAA,iBAAiB,OAAA;AACjB,2GAAA,iBAAiB,OAAA;AAKnB,mCAA0D;AAAjD,yGAAA,eAAe,OAAA;AACxB,qCAAuC;AAA9B,qGAAA,UAAU,OAAA;AAEnB,0EAA0E;AAC1E,wCAAwD;AAA/C,6FAAA,KAAK,OAAA;AAAE,8FAAA,MAAM,OAAA;AAAE,8FAAA,MAAM,OAAA;AAC9B,0CAAyC;AAAhC,8FAAA,KAAK,OAAA;AACd,wCAAuC;AAA9B,4FAAA,IAAI,OAAA;AACb,gDAA8C;AAArC,mGAAA,OAAO,OAAA;AAChB,gDAA+C;AAAtC,oGAAA,QAAQ,OAAA;AACjB,gDAA+C;AAAtC,oGAAA,QAAQ,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,6BAA0C;AAAjC,0FAAA,GAAG,OAAA;AACZ,qCAAmF;AAA1E,yGAAA,cAAc,OAAA;AACvB,6CAA8E;AAArE,+GAAA,gBAAgB,OAAA;AAAE,yGAAA,UAAU,OAAA;AACrC,mCAAoC;AAA3B,kGAAA,QAAQ,OAAA;AACjB,2BAAqE;AAA5D,+FAAA,SAAS,OAAA;AAAE,+FAAA,SAAS,OAAA;AAC7B,mCAOkB;AANhB,yGAAA,eAAe,OAAA;AACf,2GAAA,iBAAiB,OAAA;AACjB,2GAAA,iBAAiB,OAAA;AAKnB,mCAA0D;AAAjD,yGAAA,eAAe,OAAA;AACxB,qCAAuC;AAA9B,qGAAA,UAAU,OAAA;AAEnB,0EAA0E;AAC1E,wCAAwD;AAA/C,6FAAA,KAAK,OAAA;AAAE,8FAAA,MAAM,OAAA;AAAE,8FAAA,MAAM,OAAA;AAC9B,0CAAyC;AAAhC,8FAAA,KAAK,OAAA;AACd,wCAAuC;AAA9B,4FAAA,IAAI,OAAA;AACb,gDAA8C;AAArC,mGAAA,OAAO,OAAA;AAChB,gDAA+C;AAAtC,oGAAA,QAAQ,OAAA;AACjB,gDAA+C;AAAtC,oGAAA,QAAQ,OAAA;AACjB,4CAA2C;AAAlC,gGAAA,MAAM,OAAA;AACf,8CAA6C;AAApC,kGAAA,OAAO,OAAA;AAChB,wCAAuC;AAA9B,4FAAA,IAAI,OAAA;AACb,gEAA4E;AAAnE,6GAAA,SAAS,OAAA;AAAE,8GAAA,UAAU,OAAA;AAAE,0GAAA,MAAM,OAAA;AACtC,qCAOmB;AANjB,0GAAA,eAAe,OAAA;AACf,sGAAA,WAAW,OAAA;AACX,sGAAA,WAAW,OAAA;AACX,uGAAA,YAAY,OAAA;AACZ,qGAAA,UAAU,OAAA;AACV,wGAAA,aAAa,OAAA;AAEf,qCAOmB;AANjB,uGAAA,YAAY,OAAA;AACZ,wGAAA,aAAa,OAAA;AACb,uGAAA,YAAY,OAAA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type ConfigHttp } from './config-http';
|
|
2
|
+
import type { RunContext } from './context';
|
|
3
|
+
/**
|
|
4
|
+
* The project manifest `atlas init` keeps at `~/.config/<product>/atlas.json`:
|
|
5
|
+
* PUBLIC values only (ids, publishable key, hosts, redirect URIs) plus the path
|
|
6
|
+
* of each secret file. `atlas env`, `atlas preflight --instance` and
|
|
7
|
+
* `atlas client rotate` read it, so they know where a secret lives without
|
|
8
|
+
* ever holding it in the manifest.
|
|
9
|
+
*/
|
|
10
|
+
export type InstanceEnv = 'development' | 'production';
|
|
11
|
+
export interface ProjectInstance {
|
|
12
|
+
instance_id: string;
|
|
13
|
+
environment: InstanceEnv;
|
|
14
|
+
publishable_key: string;
|
|
15
|
+
frontend_api_host: string | null;
|
|
16
|
+
issuer: string | null;
|
|
17
|
+
discovery_url: string | null;
|
|
18
|
+
jwks_uri: string | null;
|
|
19
|
+
/** The file holding ATLAS_SECRET_KEY. */
|
|
20
|
+
secret_file: string;
|
|
21
|
+
oauth_client?: {
|
|
22
|
+
id: string;
|
|
23
|
+
client_id: string;
|
|
24
|
+
redirect_uris: string[];
|
|
25
|
+
secret_file: string | null;
|
|
26
|
+
};
|
|
27
|
+
webhook?: {
|
|
28
|
+
id: string;
|
|
29
|
+
url: string;
|
|
30
|
+
secret_file: string | null;
|
|
31
|
+
};
|
|
32
|
+
custom_domain?: {
|
|
33
|
+
host: string;
|
|
34
|
+
cname_target: string | null;
|
|
35
|
+
status: string;
|
|
36
|
+
};
|
|
37
|
+
email?: {
|
|
38
|
+
mode: 'managed' | 'development' | 'none';
|
|
39
|
+
from_address?: string | null;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export interface ProjectManifest {
|
|
43
|
+
product: string;
|
|
44
|
+
api_url: string;
|
|
45
|
+
instances: Partial<Record<InstanceEnv, ProjectInstance>>;
|
|
46
|
+
}
|
|
47
|
+
export declare const MANIFEST_FILE = "atlas.json";
|
|
48
|
+
export declare function manifestPath(env: NodeJS.ProcessEnv, product: string): string;
|
|
49
|
+
export declare function readManifest(env: NodeJS.ProcessEnv, product: string): ProjectManifest | undefined;
|
|
50
|
+
export declare function writeManifest(env: NodeJS.ProcessEnv, manifest: ProjectManifest): string;
|
|
51
|
+
/** Find the project + instance an instance id belongs to, scanning every product directory. */
|
|
52
|
+
export declare function findInstance(env: NodeJS.ProcessEnv, instanceId: string, product?: string): {
|
|
53
|
+
manifest: ProjectManifest;
|
|
54
|
+
instance: ProjectInstance;
|
|
55
|
+
} | undefined;
|
|
56
|
+
export declare function parseInstanceEnv(raw: string | undefined, fallback: InstanceEnv): InstanceEnv;
|
|
57
|
+
/** The raw HTTP client for a credential (the context's, or the real one). */
|
|
58
|
+
export declare function httpFor(ctx: RunContext, opts: {
|
|
59
|
+
secretKey: string;
|
|
60
|
+
apiUrl: string;
|
|
61
|
+
}): ConfigHttp;
|
|
62
|
+
/** Read the instance secret key from the project's secret file. */
|
|
63
|
+
export declare function instanceSecretFromFile(instance: ProjectInstance): string;
|
package/dist/project.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MANIFEST_FILE = void 0;
|
|
4
|
+
exports.manifestPath = manifestPath;
|
|
5
|
+
exports.readManifest = readManifest;
|
|
6
|
+
exports.writeManifest = writeManifest;
|
|
7
|
+
exports.findInstance = findInstance;
|
|
8
|
+
exports.parseInstanceEnv = parseInstanceEnv;
|
|
9
|
+
exports.httpFor = httpFor;
|
|
10
|
+
exports.instanceSecretFromFile = instanceSecretFromFile;
|
|
11
|
+
const node_fs_1 = require("node:fs");
|
|
12
|
+
const node_os_1 = require("node:os");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const config_http_1 = require("./config-http");
|
|
15
|
+
const errors_1 = require("./errors");
|
|
16
|
+
const secrets_1 = require("./secrets");
|
|
17
|
+
exports.MANIFEST_FILE = 'atlas.json';
|
|
18
|
+
function manifestPath(env, product) {
|
|
19
|
+
return (0, node_path_1.join)((0, secrets_1.productDir)(env, product), exports.MANIFEST_FILE);
|
|
20
|
+
}
|
|
21
|
+
function readManifest(env, product) {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse((0, node_fs_1.readFileSync)(manifestPath(env, product), 'utf8'));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function writeManifest(env, manifest) {
|
|
30
|
+
const dir = (0, secrets_1.productDir)(env, manifest.product);
|
|
31
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true, mode: 0o700 });
|
|
32
|
+
const path = (0, node_path_1.join)(dir, exports.MANIFEST_FILE);
|
|
33
|
+
(0, node_fs_1.writeFileSync)(path, JSON.stringify(manifest, null, 2) + '\n', { mode: 0o600 });
|
|
34
|
+
(0, node_fs_1.chmodSync)(path, 0o600);
|
|
35
|
+
return path;
|
|
36
|
+
}
|
|
37
|
+
/** Find the project + instance an instance id belongs to, scanning every product directory. */
|
|
38
|
+
function findInstance(env, instanceId, product) {
|
|
39
|
+
const products = product ? [product] : listProducts(env);
|
|
40
|
+
for (const p of products) {
|
|
41
|
+
const m = readManifest(env, p);
|
|
42
|
+
for (const inst of Object.values(m?.instances ?? {})) {
|
|
43
|
+
if (inst && inst.instance_id === instanceId)
|
|
44
|
+
return { manifest: m, instance: inst };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
function listProducts(env) {
|
|
50
|
+
const base = env.XDG_CONFIG_HOME?.trim() || (0, node_path_1.join)(env.HOME?.trim() || (0, node_os_1.homedir)(), '.config');
|
|
51
|
+
try {
|
|
52
|
+
return (0, node_fs_1.readdirSync)(base).filter((d) => {
|
|
53
|
+
try {
|
|
54
|
+
(0, node_fs_1.readFileSync)((0, node_path_1.join)(base, d, exports.MANIFEST_FILE));
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function parseInstanceEnv(raw, fallback) {
|
|
67
|
+
if (raw === undefined)
|
|
68
|
+
return fallback;
|
|
69
|
+
if (raw !== 'development' && raw !== 'production') {
|
|
70
|
+
throw new errors_1.CliError(`--instance-env must be development or production, got '${raw}'.`);
|
|
71
|
+
}
|
|
72
|
+
return raw;
|
|
73
|
+
}
|
|
74
|
+
/** The raw HTTP client for a credential (the context's, or the real one). */
|
|
75
|
+
function httpFor(ctx, opts) {
|
|
76
|
+
return (ctx.makeHttp ?? config_http_1.createConfigHttp)(opts);
|
|
77
|
+
}
|
|
78
|
+
/** Read the instance secret key from the project's secret file. */
|
|
79
|
+
function instanceSecretFromFile(instance) {
|
|
80
|
+
const key = (0, secrets_1.readEnvFile)(instance.secret_file)?.ATLAS_SECRET_KEY;
|
|
81
|
+
if (!key) {
|
|
82
|
+
throw new errors_1.CliError(`No ATLAS_SECRET_KEY in ${instance.secret_file}. Re-run 'atlas init' with --rotate-secret to mint a new one.`);
|
|
83
|
+
}
|
|
84
|
+
return key;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
|
package/dist/run.js
CHANGED
|
@@ -3,8 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.run = run;
|
|
4
4
|
const backend_1 = require("@atlasauth/backend");
|
|
5
5
|
const args_1 = require("./args");
|
|
6
|
+
const actions_1 = require("./commands/actions");
|
|
6
7
|
const api_keys_1 = require("./commands/api-keys");
|
|
7
8
|
const auth_1 = require("./commands/auth");
|
|
9
|
+
const config_1 = require("./commands/config");
|
|
10
|
+
const init_1 = require("./commands/init");
|
|
11
|
+
const project_commands_1 = require("./commands/project-commands");
|
|
8
12
|
const orgs_1 = require("./commands/orgs");
|
|
9
13
|
const sessions_1 = require("./commands/sessions");
|
|
10
14
|
const users_1 = require("./commands/users");
|
|
@@ -22,6 +26,12 @@ const COMMANDS = {
|
|
|
22
26
|
'api-keys': api_keys_1.apiKeys,
|
|
23
27
|
webhooks: webhooks_1.webhooks,
|
|
24
28
|
sessions: sessions_1.sessions,
|
|
29
|
+
config: config_1.config,
|
|
30
|
+
actions: actions_1.actions,
|
|
31
|
+
init: init_1.init,
|
|
32
|
+
preflight: project_commands_1.preflight,
|
|
33
|
+
env: project_commands_1.envCommand,
|
|
34
|
+
client: project_commands_1.client,
|
|
25
35
|
};
|
|
26
36
|
/**
|
|
27
37
|
* The CLI's whole control flow, as a pure function of (argv, context) → exit
|
package/dist/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { IO } from './io';
|
|
2
|
+
export declare function assertProductName(product: string): string;
|
|
3
|
+
/** `$XDG_CONFIG_HOME/<product>` (default `~/.config/<product>`). */
|
|
4
|
+
export declare function productDir(env: NodeJS.ProcessEnv, product: string): string;
|
|
5
|
+
export declare function renderDotenv(entries: Record<string, string>, comment?: string): string;
|
|
6
|
+
/** Parse a dotenv file this CLI wrote (KEY=value lines, `#` comments). */
|
|
7
|
+
export declare function parseDotenv(text: string): Record<string, string>;
|
|
8
|
+
/**
|
|
9
|
+
* Write a 0600 dotenv file inside a 0700 directory. Refuses an existing file
|
|
10
|
+
* unless `force` (the same content is not a conflict: a re-run is a no-op).
|
|
11
|
+
* Returns the path.
|
|
12
|
+
*/
|
|
13
|
+
export declare function writeSecretFile(dir: string, fileName: string, entries: Record<string, string>, opts?: {
|
|
14
|
+
force?: boolean;
|
|
15
|
+
comment?: string;
|
|
16
|
+
}): {
|
|
17
|
+
path: string;
|
|
18
|
+
written: boolean;
|
|
19
|
+
};
|
|
20
|
+
/** Read one of the CLI's env files, or undefined when it does not exist. */
|
|
21
|
+
export declare function readEnvFile(path: string): Record<string, string> | undefined;
|
|
22
|
+
/** The env files in a product directory (for `atlas env`). */
|
|
23
|
+
export declare function listEnvFiles(dir: string): string[];
|
|
24
|
+
/**
|
|
25
|
+
* The common "a secret arrived" path for single-secret commands: write it to
|
|
26
|
+
* `<dir>/<fileName>` (0600) and print the path, or — only with
|
|
27
|
+
* `--print-secret` — print it after a warning on stderr.
|
|
28
|
+
*/
|
|
29
|
+
export declare function deliverSecret(input: {
|
|
30
|
+
io: IO;
|
|
31
|
+
env: NodeJS.ProcessEnv;
|
|
32
|
+
product: string;
|
|
33
|
+
fileName: string;
|
|
34
|
+
entries: Record<string, string>;
|
|
35
|
+
label: string;
|
|
36
|
+
printSecret?: boolean;
|
|
37
|
+
force?: boolean;
|
|
38
|
+
comment?: string;
|
|
39
|
+
}): {
|
|
40
|
+
path?: string;
|
|
41
|
+
};
|
package/dist/secrets.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertProductName = assertProductName;
|
|
4
|
+
exports.productDir = productDir;
|
|
5
|
+
exports.renderDotenv = renderDotenv;
|
|
6
|
+
exports.parseDotenv = parseDotenv;
|
|
7
|
+
exports.writeSecretFile = writeSecretFile;
|
|
8
|
+
exports.readEnvFile = readEnvFile;
|
|
9
|
+
exports.listEnvFiles = listEnvFiles;
|
|
10
|
+
exports.deliverSecret = deliverSecret;
|
|
11
|
+
const node_fs_1 = require("node:fs");
|
|
12
|
+
const node_os_1 = require("node:os");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const errors_1 = require("./errors");
|
|
15
|
+
/**
|
|
16
|
+
* Secrets the CLI receives are written to disk, never printed.
|
|
17
|
+
*
|
|
18
|
+
* Every command that gets a secret back from the API (an instance secret key,
|
|
19
|
+
* an OAuth client secret, a webhook signing secret, an end-user API key) writes
|
|
20
|
+
* it to a dotenv file under `~/.config/<product>/` — directory 0700, file 0600 —
|
|
21
|
+
* and prints only the file path. `--print-secret` is the one explicit opt-out,
|
|
22
|
+
* and it prints a warning first. An existing file is never overwritten unless
|
|
23
|
+
* the command was given `--force`, so a re-run cannot silently destroy the only
|
|
24
|
+
* copy of a working secret.
|
|
25
|
+
*
|
|
26
|
+
* The product name doubles as the directory name, so it is restricted to a safe
|
|
27
|
+
* slug (no path separators, no dot-dot).
|
|
28
|
+
*/
|
|
29
|
+
const PRODUCT_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/i;
|
|
30
|
+
function assertProductName(product) {
|
|
31
|
+
if (!PRODUCT_RE.test(product)) {
|
|
32
|
+
throw new errors_1.CliError(`--product must be letters, digits, '-' or '_' (up to 63 characters), got '${product}'.`);
|
|
33
|
+
}
|
|
34
|
+
return product;
|
|
35
|
+
}
|
|
36
|
+
/** `$XDG_CONFIG_HOME/<product>` (default `~/.config/<product>`). */
|
|
37
|
+
function productDir(env, product) {
|
|
38
|
+
const base = env.XDG_CONFIG_HOME?.trim() || (0, node_path_1.join)(env.HOME?.trim() || (0, node_os_1.homedir)(), '.config');
|
|
39
|
+
return (0, node_path_1.join)(base, assertProductName(product));
|
|
40
|
+
}
|
|
41
|
+
/** Quote a dotenv value when it needs it. Secrets are token-shaped, so this is rarely used. */
|
|
42
|
+
function dotenvValue(v) {
|
|
43
|
+
return /^[A-Za-z0-9_./:@+=,-]*$/.test(v) ? v : JSON.stringify(v);
|
|
44
|
+
}
|
|
45
|
+
function renderDotenv(entries, comment) {
|
|
46
|
+
const lines = comment ? comment.split('\n').map((l) => `# ${l}`) : [];
|
|
47
|
+
for (const [k, v] of Object.entries(entries))
|
|
48
|
+
lines.push(`${k}=${dotenvValue(v)}`);
|
|
49
|
+
return lines.join('\n') + '\n';
|
|
50
|
+
}
|
|
51
|
+
/** Parse a dotenv file this CLI wrote (KEY=value lines, `#` comments). */
|
|
52
|
+
function parseDotenv(text) {
|
|
53
|
+
const out = {};
|
|
54
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
55
|
+
const line = raw.trim();
|
|
56
|
+
if (!line || line.startsWith('#'))
|
|
57
|
+
continue;
|
|
58
|
+
const i = line.indexOf('=');
|
|
59
|
+
if (i < 1)
|
|
60
|
+
continue;
|
|
61
|
+
const key = line.slice(0, i).trim();
|
|
62
|
+
let value = line.slice(i + 1).trim();
|
|
63
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
64
|
+
try {
|
|
65
|
+
value = JSON.parse(value);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
/* keep raw */
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
out[key] = value;
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Write a 0600 dotenv file inside a 0700 directory. Refuses an existing file
|
|
77
|
+
* unless `force` (the same content is not a conflict: a re-run is a no-op).
|
|
78
|
+
* Returns the path.
|
|
79
|
+
*/
|
|
80
|
+
function writeSecretFile(dir, fileName, entries, opts = {}) {
|
|
81
|
+
if (fileName.includes('/') || fileName.includes('\\') || fileName.startsWith('.')) {
|
|
82
|
+
throw new errors_1.CliError(`Refusing to write a secret file named '${fileName}'.`);
|
|
83
|
+
}
|
|
84
|
+
const path = (0, node_path_1.join)(dir, fileName);
|
|
85
|
+
const content = renderDotenv(entries, opts.comment);
|
|
86
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true, mode: 0o700 });
|
|
87
|
+
(0, node_fs_1.chmodSync)(dir, 0o700);
|
|
88
|
+
if ((0, node_fs_1.existsSync)(path)) {
|
|
89
|
+
const current = (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
90
|
+
if (current === content) {
|
|
91
|
+
(0, node_fs_1.chmodSync)(path, 0o600);
|
|
92
|
+
return { path, written: false };
|
|
93
|
+
}
|
|
94
|
+
if (!opts.force) {
|
|
95
|
+
throw new errors_1.CliError(`${path} already exists and holds a different secret. Nothing was overwritten. Move it aside, or re-run with --force to replace it.`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Write THEN chmod: `mode` is masked by umask and ignored for an existing file.
|
|
99
|
+
(0, node_fs_1.writeFileSync)(path, content, { mode: 0o600 });
|
|
100
|
+
(0, node_fs_1.chmodSync)(path, 0o600);
|
|
101
|
+
return { path, written: true };
|
|
102
|
+
}
|
|
103
|
+
/** Read one of the CLI's env files, or undefined when it does not exist. */
|
|
104
|
+
function readEnvFile(path) {
|
|
105
|
+
try {
|
|
106
|
+
return parseDotenv((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** The env files in a product directory (for `atlas env`). */
|
|
113
|
+
function listEnvFiles(dir) {
|
|
114
|
+
try {
|
|
115
|
+
return (0, node_fs_1.readdirSync)(dir)
|
|
116
|
+
.filter((f) => f.endsWith('.env'))
|
|
117
|
+
.sort();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The common "a secret arrived" path for single-secret commands: write it to
|
|
125
|
+
* `<dir>/<fileName>` (0600) and print the path, or — only with
|
|
126
|
+
* `--print-secret` — print it after a warning on stderr.
|
|
127
|
+
*/
|
|
128
|
+
function deliverSecret(input) {
|
|
129
|
+
if (input.printSecret) {
|
|
130
|
+
input.io.err(`atlas: warning: printing ${input.label} because --print-secret was given. It may end up in your shell history, terminal scrollback or CI logs.`);
|
|
131
|
+
for (const [k, v] of Object.entries(input.entries))
|
|
132
|
+
input.io.out(`${k}=${v}`);
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
135
|
+
const { path } = writeSecretFile(productDir(input.env, input.product), input.fileName, input.entries, {
|
|
136
|
+
force: input.force,
|
|
137
|
+
comment: input.comment,
|
|
138
|
+
});
|
|
139
|
+
input.io.out(`${input.label} written to ${path} (mode 0600).`);
|
|
140
|
+
return { path };
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The on-disk format for `atlas config` — the config-as-code layer. Two
|
|
3
|
+
* concerns live here, kept away from the command logic:
|
|
4
|
+
*
|
|
5
|
+
* 1. CANONICALIZATION. A value is normalised before it is written or compared
|
|
6
|
+
* so an export is byte-for-byte deterministic and a round-trip is a no-op
|
|
7
|
+
* diff. We sort object keys (the same rule Go's `json.Marshal` and the
|
|
8
|
+
* Terraform provider's `canonicalJSON` use) and PRESERVE array order — so a
|
|
9
|
+
* field whose order is meaningful (a claim mapping, an auth_config list) is
|
|
10
|
+
* never silently reordered.
|
|
11
|
+
*
|
|
12
|
+
* 2. THE CODEC. JSON is the robust default. YAML is offered for readable diffs
|
|
13
|
+
* in a repo; our YAML is a deliberately small, fully-specified subset where
|
|
14
|
+
* every map key and every scalar is JSON-encoded, so it is unambiguous to
|
|
15
|
+
* parse, round-trips exactly (newlines, URL-shaped keys, unicode and all),
|
|
16
|
+
* and is still valid YAML (JSON is a YAML subset).
|
|
17
|
+
*/
|
|
18
|
+
export type Json = string | number | boolean | null | Json[] | {
|
|
19
|
+
[k: string]: Json;
|
|
20
|
+
};
|
|
21
|
+
export type Format = 'json' | 'yaml';
|
|
22
|
+
/** A plain JSON value with all object keys recursively sorted; arrays untouched. */
|
|
23
|
+
export declare function canonicalize(value: unknown): Json;
|
|
24
|
+
/** Canonical JSON string — the equality oracle for the diff engine. */
|
|
25
|
+
export declare function canonicalString(value: unknown): string;
|
|
26
|
+
/** True when two config values are equal after canonicalization. */
|
|
27
|
+
export declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
28
|
+
/** Serialize a canonicalizable value to the chosen format, with a trailing newline. */
|
|
29
|
+
export declare function serialize(value: unknown, format: Format): string;
|
|
30
|
+
/** Parse a document written by {@link serialize}. */
|
|
31
|
+
export declare function deserialize(text: string, format: Format): Json;
|