tailar 0.4.0 → 0.5.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 +5 -2
- package/README.npm.md +5 -2
- package/dist/api/client.d.ts +22 -28
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +111 -236
- package/dist/api/client.js.map +1 -1
- package/dist/api/errors.d.ts +16 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +32 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/auth/flow.js +1 -1
- package/dist/auth/flow.js.map +1 -1
- package/dist/commands/add.d.ts +9 -0
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +63 -113
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +106 -70
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +1 -23
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/playground.d.ts.map +1 -1
- package/dist/commands/playground.js +2 -1
- package/dist/commands/playground.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +9 -4
- package/dist/commands/sync.js.map +1 -1
- package/dist/installers/agent-files.d.ts +26 -0
- package/dist/installers/agent-files.d.ts.map +1 -0
- package/dist/installers/agent-files.js +110 -0
- package/dist/installers/agent-files.js.map +1 -0
- package/dist/installers/dependencies.d.ts +10 -0
- package/dist/installers/dependencies.d.ts.map +1 -1
- package/dist/installers/dependencies.js +52 -8
- package/dist/installers/dependencies.js.map +1 -1
- package/dist/installers/files.d.ts +2 -6
- package/dist/installers/files.d.ts.map +1 -1
- package/dist/installers/files.js +30 -35
- package/dist/installers/files.js.map +1 -1
- package/dist/installers/install-feature.d.ts +25 -1
- package/dist/installers/install-feature.d.ts.map +1 -1
- package/dist/installers/install-feature.js +100 -59
- package/dist/installers/install-feature.js.map +1 -1
- package/dist/installers/managed-blocks.d.ts +26 -0
- package/dist/installers/managed-blocks.d.ts.map +1 -0
- package/dist/installers/managed-blocks.js +47 -0
- package/dist/installers/managed-blocks.js.map +1 -0
- package/dist/installers/post-install.d.ts +12 -0
- package/dist/installers/post-install.d.ts.map +1 -1
- package/dist/installers/post-install.js +19 -0
- package/dist/installers/post-install.js.map +1 -1
- package/dist/installers/setup-guide.d.ts +38 -0
- package/dist/installers/setup-guide.d.ts.map +1 -0
- package/dist/installers/setup-guide.js +206 -0
- package/dist/installers/setup-guide.js.map +1 -0
- package/dist/installers/variant-switch.d.ts +34 -0
- package/dist/installers/variant-switch.d.ts.map +1 -0
- package/dist/installers/variant-switch.js +139 -0
- package/dist/installers/variant-switch.js.map +1 -0
- package/dist/installers/when.d.ts +22 -0
- package/dist/installers/when.d.ts.map +1 -0
- package/dist/installers/when.js +35 -0
- package/dist/installers/when.js.map +1 -0
- package/dist/registry/query.d.ts +6 -1
- package/dist/registry/query.d.ts.map +1 -1
- package/dist/registry/query.js +22 -0
- package/dist/registry/query.js.map +1 -1
- package/dist/types/config.d.ts +2 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/registry.d.ts +65 -6
- package/dist/types/registry.d.ts.map +1 -1
- package/dist/types/wizard.d.ts +34 -8
- package/dist/types/wizard.d.ts.map +1 -1
- package/dist/types/wizard.js +74 -40
- package/dist/types/wizard.js.map +1 -1
- package/dist/utils/clack-wizard.d.ts +3 -1
- package/dist/utils/clack-wizard.d.ts.map +1 -1
- package/dist/utils/clack-wizard.js +205 -107
- package/dist/utils/clack-wizard.js.map +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +12 -5
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/init-flags.d.ts +43 -8
- package/dist/utils/init-flags.d.ts.map +1 -1
- package/dist/utils/init-flags.js +119 -32
- package/dist/utils/init-flags.js.map +1 -1
- package/dist/utils/local-options.js +1 -1
- package/dist/utils/local-options.js.map +1 -1
- package/dist/utils/next-steps.d.ts +9 -0
- package/dist/utils/next-steps.d.ts.map +1 -0
- package/dist/utils/next-steps.js +15 -0
- package/dist/utils/next-steps.js.map +1 -0
- package/dist/utils/presets.d.ts +34 -0
- package/dist/utils/presets.d.ts.map +1 -0
- package/dist/utils/presets.js +82 -0
- package/dist/utils/presets.js.map +1 -0
- package/dist/utils/recipe-id.d.ts +19 -0
- package/dist/utils/recipe-id.d.ts.map +1 -0
- package/dist/utils/recipe-id.js +72 -0
- package/dist/utils/recipe-id.js.map +1 -0
- package/dist/utils/recipe.d.ts +49 -0
- package/dist/utils/recipe.d.ts.map +1 -0
- package/dist/utils/recipe.js +227 -0
- package/dist/utils/recipe.js.map +1 -0
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -62,10 +62,13 @@ tailar init my-app -y --modules auth --db postgres --email resend --auth organiz
|
|
|
62
62
|
| --- | --- | --- |
|
|
63
63
|
| `-y, --yes` | Skip prompts. In an empty directory this scaffolds without the wizard | `false` |
|
|
64
64
|
| `[project]` / `--name` | Directory name for a new app | `my-app` when non-interactive |
|
|
65
|
-
| `--
|
|
65
|
+
| `--preset <id>` | `saas`, `saas-ai`, `marketing`, or `custom` | none (flags only) |
|
|
66
|
+
| `--modules <list>` | `database,email,auth,payments,storage,ai` (replaces the preset's modules) | the preset's modules, else none |
|
|
66
67
|
| `--db <provider>` | `postgres`, `mysql`, or `mongodb` | `postgres` when database is included |
|
|
67
68
|
| `--email <provider>` | `resend`, `postmark`, or `sendgrid` | `resend` when email is included |
|
|
68
69
|
| `--auth <structure>` | `user-based` or `organization-based` | `user-based` when auth is included |
|
|
70
|
+
| `--storage-provider <p>` | Storage provider (adds `storage`), e.g. `r2` | `r2` when storage is included |
|
|
71
|
+
| `--billing-provider <p>` | Billing provider (adds `payments`), from the registry: `stripe`, `lemonsqueezy`, `paddle` | `stripe` when payments is included |
|
|
69
72
|
| `--skip-install` | Skip npm install and post-install commands | `false` |
|
|
70
73
|
|
|
71
74
|
**What it does:**
|
|
@@ -203,7 +206,7 @@ npm run build
|
|
|
203
206
|
## 📚 Learn More
|
|
204
207
|
|
|
205
208
|
- **Docs**: [https://www.tailar.dev/docs](https://www.tailar.dev/docs)
|
|
206
|
-
- **Report Issues**: [GitHub Issues](https://github.com/tailar-dev/tailar/issues)
|
|
209
|
+
- **Report Issues**: [GitHub Issues](https://github.com/tailar-dev/tailar-all/issues)
|
|
207
210
|
|
|
208
211
|
## 📄 License
|
|
209
212
|
|
package/README.npm.md
CHANGED
|
@@ -62,10 +62,13 @@ tailar init my-app -y --modules auth --db postgres --email resend --auth organiz
|
|
|
62
62
|
| --- | --- | --- |
|
|
63
63
|
| `-y, --yes` | Skip prompts. In an empty directory this scaffolds without the wizard | `false` |
|
|
64
64
|
| `[project]` / `--name` | Directory name for a new app | `my-app` when non-interactive |
|
|
65
|
-
| `--
|
|
65
|
+
| `--preset <id>` | `saas`, `saas-ai`, `marketing`, or `custom` | none (flags only) |
|
|
66
|
+
| `--modules <list>` | `database,email,auth,payments,storage,ai` (replaces the preset's modules) | the preset's modules, else none |
|
|
66
67
|
| `--db <provider>` | `postgres`, `mysql`, or `mongodb` | `postgres` when database is included |
|
|
67
68
|
| `--email <provider>` | `resend`, `postmark`, or `sendgrid` | `resend` when email is included |
|
|
68
69
|
| `--auth <structure>` | `user-based` or `organization-based` | `user-based` when auth is included |
|
|
70
|
+
| `--storage-provider <p>` | Storage provider (adds `storage`), e.g. `r2` | `r2` when storage is included |
|
|
71
|
+
| `--billing-provider <p>` | Billing provider (adds `payments`), from the registry: `stripe`, `lemonsqueezy`, `paddle` | `stripe` when payments is included |
|
|
69
72
|
| `--skip-install` | Skip npm install and post-install commands | `false` |
|
|
70
73
|
|
|
71
74
|
**What it does:**
|
|
@@ -203,7 +206,7 @@ npm run build
|
|
|
203
206
|
## 📚 Learn More
|
|
204
207
|
|
|
205
208
|
- **Docs**: [https://www.tailar.dev/docs](https://www.tailar.dev/docs)
|
|
206
|
-
- **Report Issues**: [GitHub Issues](https://github.com/tailar-dev/tailar/issues)
|
|
209
|
+
- **Report Issues**: [GitHub Issues](https://github.com/tailar-dev/tailar-all/issues)
|
|
207
210
|
|
|
208
211
|
## 📄 License
|
|
209
212
|
|
package/dist/api/client.d.ts
CHANGED
|
@@ -3,45 +3,39 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles all API interactions with the Tailar web API
|
|
5
5
|
*/
|
|
6
|
+
import fetch from "node-fetch";
|
|
6
7
|
import type { Registry } from "../types/index.js";
|
|
7
|
-
interface ComponentMetadata {
|
|
8
|
-
name: string;
|
|
9
|
-
type: string;
|
|
10
|
-
requiresSubscription: boolean;
|
|
11
|
-
subscriptionTier: number;
|
|
12
|
-
isAccessible?: boolean;
|
|
13
|
-
reason?: string;
|
|
14
|
-
files: any[];
|
|
15
|
-
}
|
|
16
8
|
/**
|
|
17
|
-
* Fetch registry.json from API
|
|
9
|
+
* Fetch registry.json from API (public: the files behind it need the licence)
|
|
18
10
|
*/
|
|
19
11
|
export declare function fetchRegistry(options?: {
|
|
20
12
|
forceRefresh?: boolean;
|
|
21
13
|
version?: string;
|
|
22
14
|
}): Promise<Registry>;
|
|
23
15
|
/**
|
|
24
|
-
*
|
|
16
|
+
* GET an endpoint that needs a logged-in licence holder.
|
|
17
|
+
*
|
|
18
|
+
* Logs in first when there is no valid token (the device flow prints the
|
|
19
|
+
* verification URL and waits, so it also works in CI). A 401 logs in again
|
|
20
|
+
* once; a 403 `licence_required` throws LicenceRequiredError. Any other
|
|
21
|
+
* status is returned for the caller to handle.
|
|
25
22
|
*/
|
|
26
|
-
export declare function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
export declare function licensedGet(endpoint: string): Promise<Awaited<ReturnType<typeof fetch>>>;
|
|
24
|
+
/** Forget a successful licence check (tests). */
|
|
25
|
+
export declare function resetLicenceCheck(): void;
|
|
26
|
+
/** Record that a licence-gated call succeeded, so later calls skip the check. */
|
|
27
|
+
export declare function markLicenceVerified(): void;
|
|
30
28
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* The one licence check. Registry files need a logged-in licence holder:
|
|
30
|
+
* logs in when needed and throws LicenceRequiredError when the account has no
|
|
31
|
+
* licence. Local registry mode (development and CI) skips it. Checked once per
|
|
32
|
+
* process.
|
|
33
33
|
*/
|
|
34
|
-
export declare function
|
|
34
|
+
export declare function requireLicence(): Promise<void>;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
* @param registryEntry - Optional registry entry to check subscription requirements before API call
|
|
36
|
+
* Fetch a single component file from API (licence required)
|
|
38
37
|
*/
|
|
39
|
-
export declare function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}): Promise<{
|
|
43
|
-
accessible: boolean;
|
|
44
|
-
reason?: string;
|
|
45
|
-
}>;
|
|
46
|
-
export {};
|
|
38
|
+
export declare function fetchComponentFile(type: "feature" | "block" | "component" | "template", name: string, filePath: string, options?: {
|
|
39
|
+
variant?: string;
|
|
40
|
+
}): Promise<string>;
|
|
47
41
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAC;AAM/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AA0GlD;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,OAAO,CAAC,QAAQ,CAAC,CAoBnB;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAiD5C;AAID,iDAAiD;AACjD,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAKD;;;;;GAKG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAYpD;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,WAAW,GAAG,UAAU,EACpD,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACjC,OAAO,CAAC,MAAM,CAAC,CA6BjB"}
|
package/dist/api/client.js
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
import fetch from "node-fetch";
|
|
7
7
|
import { logger } from "../utils/logger.js";
|
|
8
8
|
import { getAuthHeaders, authenticate, isAuthenticated } from "../auth/flow.js";
|
|
9
|
-
import {
|
|
9
|
+
import { clearToken } from "../auth/storage.js";
|
|
10
|
+
import { getLocalRegistry } from "../registry/local-mode.js";
|
|
11
|
+
import { LicenceRequiredError } from "./errors.js";
|
|
10
12
|
// Lazy-load API_BASE_URL to ensure .env file is loaded first
|
|
11
13
|
// This is a function because .env file is loaded after module imports
|
|
12
14
|
function getApiBaseUrl() {
|
|
@@ -16,94 +18,48 @@ const MAX_RETRIES = 3;
|
|
|
16
18
|
const RETRY_DELAY = 1000; // 1 second
|
|
17
19
|
/** Avoid infinite spinner when API is unreachable (default tailar.dev or wrong host). */
|
|
18
20
|
const DEFAULT_REQUEST_TIMEOUT_MS = 45_000;
|
|
21
|
+
/** fetch with a timeout, so an unreachable API does not leave a spinner running forever. */
|
|
22
|
+
async function timedFetch(url, init = {}) {
|
|
23
|
+
const timeoutMs = Number(process.env.TAILAR_CLI_REQUEST_TIMEOUT_MS) ||
|
|
24
|
+
DEFAULT_REQUEST_TIMEOUT_MS;
|
|
25
|
+
const abortController = new AbortController();
|
|
26
|
+
const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
|
|
27
|
+
try {
|
|
28
|
+
return await fetch(url, { ...init, signal: abortController.signal });
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
const aborted = e instanceof Error &&
|
|
32
|
+
(e.name === "AbortError" || e.message.includes("aborted"));
|
|
33
|
+
if (aborted) {
|
|
34
|
+
throw new Error(`Request timed out after ${timeoutMs}ms (${url}). ` +
|
|
35
|
+
`If you run the Tailar web app locally, set API_BASE_URL in this project's .env, e.g. API_BASE_URL=http://localhost:3000/api/cli`);
|
|
36
|
+
}
|
|
37
|
+
throw e;
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
clearTimeout(timeoutId);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
19
43
|
/**
|
|
20
|
-
*
|
|
44
|
+
* Public GET with retry logic (the registry listing needs no login).
|
|
21
45
|
*/
|
|
22
46
|
async function apiRequest(endpoint, options = {}) {
|
|
23
|
-
const {
|
|
47
|
+
const { retries = MAX_RETRIES } = options;
|
|
24
48
|
let attempt = 0;
|
|
25
49
|
let lastError = null;
|
|
26
50
|
while (attempt <= retries) {
|
|
27
51
|
try {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const authHeaders = await getAuthHeaders();
|
|
37
|
-
Object.assign(headers, authHeaders);
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
// Not authenticated, try to authenticate
|
|
41
|
-
if (error instanceof Error &&
|
|
42
|
-
error.message.includes("Not authenticated")) {
|
|
43
|
-
logger.info("Authentication required. Starting authentication flow...");
|
|
44
|
-
await authenticate();
|
|
45
|
-
// Retry with new auth headers
|
|
46
|
-
const authHeaders = await getAuthHeaders();
|
|
47
|
-
Object.assign(headers, authHeaders);
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
throw error;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const fetchInit = {
|
|
55
|
-
method: method || "GET",
|
|
56
|
-
headers,
|
|
57
|
-
};
|
|
58
|
-
if (body) {
|
|
59
|
-
fetchInit.body = body;
|
|
60
|
-
}
|
|
61
|
-
const fullUrl = `${getApiBaseUrl()}${endpoint}`;
|
|
62
|
-
const timeoutMs = Number(process.env.TAILAR_CLI_REQUEST_TIMEOUT_MS) ||
|
|
63
|
-
DEFAULT_REQUEST_TIMEOUT_MS;
|
|
64
|
-
const abortController = new AbortController();
|
|
65
|
-
const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
|
|
66
|
-
fetchInit.signal = abortController.signal;
|
|
67
|
-
let response;
|
|
68
|
-
try {
|
|
69
|
-
response = await fetch(fullUrl, fetchInit);
|
|
70
|
-
}
|
|
71
|
-
catch (e) {
|
|
72
|
-
const aborted = e instanceof Error &&
|
|
73
|
-
(e.name === "AbortError" || e.message.includes("aborted"));
|
|
74
|
-
if (aborted) {
|
|
75
|
-
throw new Error(`Request timed out after ${timeoutMs}ms (${fullUrl}). ` +
|
|
76
|
-
`If you run the Tailar web app locally, set API_BASE_URL in this project's .env, e.g. API_BASE_URL=http://localhost:3000/api/cli`);
|
|
77
|
-
}
|
|
78
|
-
throw e;
|
|
79
|
-
}
|
|
80
|
-
finally {
|
|
81
|
-
clearTimeout(timeoutId);
|
|
82
|
-
}
|
|
83
|
-
// Handle authentication errors
|
|
84
|
-
if (response.status === 401) {
|
|
85
|
-
if (requireAuth && attempt === 0) {
|
|
86
|
-
// Try to re-authenticate once
|
|
87
|
-
logger.warn("Authentication expired. Re-authenticating...");
|
|
88
|
-
await authenticate();
|
|
89
|
-
attempt++;
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
throw new Error("Authentication failed. Please run authentication again.");
|
|
93
|
-
}
|
|
94
|
-
// Handle subscription errors
|
|
95
|
-
if (response.status === 403) {
|
|
96
|
-
const errorData = (await response.json().catch(() => ({})));
|
|
97
|
-
const reason = errorData.reason || errorData.error || "Subscription required";
|
|
98
|
-
throw new Error(`Access denied: ${reason}\n` +
|
|
99
|
-
`Visit https://tailar.dev/choose-plan to upgrade your subscription.`);
|
|
100
|
-
}
|
|
101
|
-
// Handle not found errors
|
|
52
|
+
const response = await timedFetch(`${getApiBaseUrl()}${endpoint}`, {
|
|
53
|
+
method: "GET",
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
"User-Agent": "tailar-cli",
|
|
57
|
+
},
|
|
58
|
+
});
|
|
102
59
|
if (response.status === 404) {
|
|
103
60
|
const errorData = (await response.json().catch(() => ({})));
|
|
104
61
|
throw new Error(errorData.error || "Resource not found");
|
|
105
62
|
}
|
|
106
|
-
// Handle other errors
|
|
107
63
|
if (!response.ok) {
|
|
108
64
|
const errorData = (await response.json().catch(() => ({})));
|
|
109
65
|
throw new Error(errorData.error || `API request failed: ${response.statusText}`);
|
|
@@ -112,10 +68,7 @@ async function apiRequest(endpoint, options = {}) {
|
|
|
112
68
|
}
|
|
113
69
|
catch (error) {
|
|
114
70
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
115
|
-
|
|
116
|
-
if (lastError.message.includes("Authentication") ||
|
|
117
|
-
lastError.message.includes("Access denied") ||
|
|
118
|
-
lastError.message.includes("not found")) {
|
|
71
|
+
if (lastError.message.includes("not found")) {
|
|
119
72
|
throw lastError;
|
|
120
73
|
}
|
|
121
74
|
// Retry on network errors
|
|
@@ -130,7 +83,7 @@ async function apiRequest(endpoint, options = {}) {
|
|
|
130
83
|
throw lastError || new Error("API request failed");
|
|
131
84
|
}
|
|
132
85
|
/**
|
|
133
|
-
* Fetch registry.json from API
|
|
86
|
+
* Fetch registry.json from API (public: the files behind it need the licence)
|
|
134
87
|
*/
|
|
135
88
|
export async function fetchRegistry(options = {}) {
|
|
136
89
|
try {
|
|
@@ -139,12 +92,7 @@ export async function fetchRegistry(options = {}) {
|
|
|
139
92
|
params.append("version", options.version);
|
|
140
93
|
}
|
|
141
94
|
const endpoint = `/registry${params.toString() ? `?${params.toString()}` : ""}`;
|
|
142
|
-
|
|
143
|
-
const registry = await apiRequest(endpoint, {
|
|
144
|
-
requireAuth: false,
|
|
145
|
-
method: "GET",
|
|
146
|
-
});
|
|
147
|
-
return registry;
|
|
95
|
+
return await apiRequest(endpoint);
|
|
148
96
|
}
|
|
149
97
|
catch (error) {
|
|
150
98
|
logger.error(`Failed to fetch registry: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -152,178 +100,105 @@ export async function fetchRegistry(options = {}) {
|
|
|
152
100
|
}
|
|
153
101
|
}
|
|
154
102
|
/**
|
|
155
|
-
*
|
|
103
|
+
* GET an endpoint that needs a logged-in licence holder.
|
|
104
|
+
*
|
|
105
|
+
* Logs in first when there is no valid token (the device flow prints the
|
|
106
|
+
* verification URL and waits, so it also works in CI). A 401 logs in again
|
|
107
|
+
* once; a 403 `licence_required` throws LicenceRequiredError. Any other
|
|
108
|
+
* status is returned for the caller to handle.
|
|
156
109
|
*/
|
|
157
|
-
export async function
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
110
|
+
export async function licensedGet(endpoint) {
|
|
111
|
+
if (!(await isAuthenticated())) {
|
|
112
|
+
logger.info("Logging in to Tailar...");
|
|
113
|
+
await authenticate();
|
|
114
|
+
}
|
|
115
|
+
for (let attempt = 0;; attempt++) {
|
|
116
|
+
let authHeaders;
|
|
117
|
+
try {
|
|
118
|
+
authHeaders = await getAuthHeaders();
|
|
163
119
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (options.requireAuth === true) {
|
|
168
|
-
// Check if already authenticated
|
|
169
|
-
if (!(await isAuthenticated())) {
|
|
170
|
-
logger.info("Authentication required. Starting authentication flow...");
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (attempt === 0) {
|
|
122
|
+
logger.warn("Login expired. Logging in again...");
|
|
171
123
|
await authenticate();
|
|
124
|
+
continue;
|
|
172
125
|
}
|
|
173
|
-
|
|
174
|
-
const response = await fetch(`${getApiBaseUrl()}${endpoint}`, {
|
|
175
|
-
method: "GET",
|
|
176
|
-
headers: {
|
|
177
|
-
"User-Agent": "tailar-cli",
|
|
178
|
-
...(await getAuthHeaders()),
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
if (!response.ok) {
|
|
182
|
-
if (response.status === 403) {
|
|
183
|
-
const errorData = (await response.json().catch(() => ({})));
|
|
184
|
-
const reason = errorData.reason || errorData.error || "Subscription required";
|
|
185
|
-
throw new Error(`Access denied: ${reason}\n` +
|
|
186
|
-
`Visit https://tailar.dev/choose-plan to upgrade your subscription.`);
|
|
187
|
-
}
|
|
188
|
-
throw new Error(`Failed to fetch file: ${response.statusText}`);
|
|
189
|
-
}
|
|
190
|
-
return await response.text();
|
|
126
|
+
throw error;
|
|
191
127
|
}
|
|
192
|
-
|
|
193
|
-
// This allows free components to work without authentication
|
|
194
|
-
let response = await fetch(`${getApiBaseUrl()}${endpoint}`, {
|
|
128
|
+
const response = await timedFetch(`${getApiBaseUrl()}${endpoint}`, {
|
|
195
129
|
method: "GET",
|
|
196
|
-
headers: {
|
|
197
|
-
"User-Agent": "tailar-cli",
|
|
198
|
-
},
|
|
130
|
+
headers: { "User-Agent": "tailar-cli", ...authHeaders },
|
|
199
131
|
});
|
|
200
|
-
// Handle 401 responses - component requires authentication
|
|
201
132
|
if (response.status === 401) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
method: "GET",
|
|
208
|
-
headers: {
|
|
209
|
-
"User-Agent": "tailar-cli",
|
|
210
|
-
...(await getAuthHeaders()),
|
|
211
|
-
},
|
|
212
|
-
});
|
|
213
|
-
if (!retryResponse.ok) {
|
|
214
|
-
throw new Error(`Failed to fetch file: ${retryResponse.statusText}`);
|
|
133
|
+
if (attempt === 0) {
|
|
134
|
+
logger.warn("Login expired. Logging in again...");
|
|
135
|
+
await clearToken();
|
|
136
|
+
await authenticate();
|
|
137
|
+
continue;
|
|
215
138
|
}
|
|
216
|
-
|
|
139
|
+
throw new Error("Login failed. Run the command again to log in with your Tailar account.");
|
|
217
140
|
}
|
|
218
141
|
if (response.status === 403) {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (!response.ok) {
|
|
225
|
-
throw new Error(`Failed to fetch file: ${response.statusText}`);
|
|
226
|
-
}
|
|
227
|
-
return await response.text();
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
if (error instanceof Error && error.message.includes("Access denied")) {
|
|
231
|
-
throw error;
|
|
142
|
+
const body = (await response.json().catch(() => ({})));
|
|
143
|
+
if (!body.error || body.error === "licence_required") {
|
|
144
|
+
throw new LicenceRequiredError(body.buyUrl);
|
|
145
|
+
}
|
|
146
|
+
throw new Error(`Access denied: ${body.error}`);
|
|
232
147
|
}
|
|
233
|
-
|
|
234
|
-
throw error;
|
|
148
|
+
return response;
|
|
235
149
|
}
|
|
236
150
|
}
|
|
151
|
+
let licenceVerified = false;
|
|
152
|
+
/** Forget a successful licence check (tests). */
|
|
153
|
+
export function resetLicenceCheck() {
|
|
154
|
+
licenceVerified = false;
|
|
155
|
+
}
|
|
156
|
+
/** Record that a licence-gated call succeeded, so later calls skip the check. */
|
|
157
|
+
export function markLicenceVerified() {
|
|
158
|
+
licenceVerified = true;
|
|
159
|
+
}
|
|
160
|
+
/** Entry used to check the licence: any licence-gated route answers 401/403 before it looks the entry up. */
|
|
161
|
+
const LICENCE_PROBE = "/components/feature/shared";
|
|
237
162
|
/**
|
|
238
|
-
*
|
|
239
|
-
*
|
|
163
|
+
* The one licence check. Registry files need a logged-in licence holder:
|
|
164
|
+
* logs in when needed and throws LicenceRequiredError when the account has no
|
|
165
|
+
* licence. Local registry mode (development and CI) skips it. Checked once per
|
|
166
|
+
* process.
|
|
240
167
|
*/
|
|
241
|
-
export async function
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
catch (error) {
|
|
252
|
-
logger.error(`Failed to fetch component metadata: ${error instanceof Error ? error.message : String(error)}`);
|
|
253
|
-
throw error;
|
|
168
|
+
export async function requireLicence() {
|
|
169
|
+
if (getLocalRegistry()?.enabled)
|
|
170
|
+
return;
|
|
171
|
+
if (licenceVerified)
|
|
172
|
+
return;
|
|
173
|
+
const response = await licensedGet(LICENCE_PROBE);
|
|
174
|
+
// 404 means the guard passed and only the probe entry is missing.
|
|
175
|
+
if (!response.ok && response.status !== 404) {
|
|
176
|
+
throw new Error(`Could not verify your licence: ${response.status} ${response.statusText}`);
|
|
254
177
|
}
|
|
178
|
+
licenceVerified = true;
|
|
255
179
|
}
|
|
256
180
|
/**
|
|
257
|
-
*
|
|
258
|
-
* @param registryEntry - Optional registry entry to check subscription requirements before API call
|
|
181
|
+
* Fetch a single component file from API (licence required)
|
|
259
182
|
*/
|
|
260
|
-
export async function
|
|
261
|
-
// If registry entry is provided and component doesn't require subscription, allow access immediately
|
|
262
|
-
if (registryEntry &&
|
|
263
|
-
(!registryEntry.requiresSubscription ||
|
|
264
|
-
(registryEntry.subscriptionTier || 0) === 0)) {
|
|
265
|
-
return { accessible: true };
|
|
266
|
-
}
|
|
183
|
+
export async function fetchComponentFile(type, name, filePath, options = {}) {
|
|
267
184
|
try {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return { accessible: true };
|
|
185
|
+
const params = new URLSearchParams();
|
|
186
|
+
params.append("path", filePath);
|
|
187
|
+
if (options.variant) {
|
|
188
|
+
params.append("variant", options.variant);
|
|
273
189
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (metadata.isAccessible !== true) {
|
|
278
|
-
// If isAccessible is explicitly false, use the reason from API
|
|
279
|
-
if (metadata.isAccessible === false && metadata.reason) {
|
|
280
|
-
return {
|
|
281
|
-
accessible: false,
|
|
282
|
-
reason: metadata.reason,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
// isAccessible is false with no reason: server treated the request as anonymous.
|
|
286
|
-
// This happens when a locally-valid token was revoked server-side.
|
|
287
|
-
// If we have stored credentials, clear them and trigger a fresh auth flow.
|
|
288
|
-
const storedToken = await getStoredToken();
|
|
289
|
-
if (storedToken) {
|
|
290
|
-
logger.warn("Session revoked or expired. Re-authenticating...");
|
|
291
|
-
await clearToken();
|
|
292
|
-
await authenticate();
|
|
293
|
-
// Retry once with the new token
|
|
294
|
-
const retryMetadata = await fetchComponentMetadata(type, name, true);
|
|
295
|
-
if (!retryMetadata.requiresSubscription ||
|
|
296
|
-
retryMetadata.isAccessible === true) {
|
|
297
|
-
return { accessible: true };
|
|
298
|
-
}
|
|
299
|
-
return {
|
|
300
|
-
accessible: false,
|
|
301
|
-
reason: retryMetadata.reason ?? "Subscription required.",
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
return {
|
|
305
|
-
accessible: false,
|
|
306
|
-
reason: "Authentication and subscription required. Please authenticate first.",
|
|
307
|
-
};
|
|
190
|
+
const response = await licensedGet(`/components/${type}/${name}/file?${params.toString()}`);
|
|
191
|
+
if (!response.ok) {
|
|
192
|
+
throw new Error(`Failed to fetch file ${type}s/${name}/${filePath}: ${response.status} ${response.statusText}`);
|
|
308
193
|
}
|
|
309
|
-
|
|
194
|
+
licenceVerified = true;
|
|
195
|
+
return await response.text();
|
|
310
196
|
}
|
|
311
197
|
catch (error) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
error.message.includes("Authentication required"))) {
|
|
317
|
-
return {
|
|
318
|
-
accessible: false,
|
|
319
|
-
reason: "Authentication required. Please authenticate first.",
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
// For other errors, deny access
|
|
323
|
-
return {
|
|
324
|
-
accessible: false,
|
|
325
|
-
reason: error instanceof Error ? error.message : "Failed to verify access",
|
|
326
|
-
};
|
|
198
|
+
if (error instanceof LicenceRequiredError)
|
|
199
|
+
throw error;
|
|
200
|
+
logger.error(`Failed to fetch component file: ${error instanceof Error ? error.message : String(error)}`);
|
|
201
|
+
throw error;
|
|
327
202
|
}
|
|
328
203
|
}
|
|
329
204
|
//# sourceMappingURL=client.js.map
|
package/dist/api/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGnD,6DAA6D;AAC7D,sEAAsE;AACtE,SAAS,aAAa;IACpB,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,4BAA4B,CAAC;AAClE,CAAC;AACD,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,WAAW;AACrC,yFAAyF;AACzF,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAM1C,4FAA4F;AAC5F,KAAK,UAAU,UAAU,CACvB,GAAW,EACX,OAAoC,EAAE;IAEtC,MAAM,SAAS,GACb,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC;QACjD,0BAA0B,CAAC;IAC7B,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;IAC9C,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IACvE,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,OAAO,GACX,CAAC,YAAY,KAAK;YAClB,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;QAC7D,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CACb,2BAA2B,SAAS,OAAO,GAAG,KAAK;gBACjD,iIAAiI,CACpI,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,CAAC;IACV,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,UAAU,CACvB,QAAgB,EAChB,UAAwB,EAAE;IAE1B,MAAM,EAAE,OAAO,GAAG,WAAW,EAAE,GAAG,OAAO,CAAC;IAE1C,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,SAAS,GAAiB,IAAI,CAAC;IAEnC,OAAO,OAAO,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,aAAa,EAAE,GAAG,QAAQ,EAAE,EAAE;gBACjE,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,YAAY,EAAE,YAAY;iBAC3B;aACF,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAEzD,CAAC;gBACF,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,IAAI,oBAAoB,CAAC,CAAC;YAC3D,CAAC;YAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAEzD,CAAC;gBACF,MAAM,IAAI,KAAK,CACb,SAAS,CAAC,KAAK,IAAI,uBAAuB,QAAQ,CAAC,UAAU,EAAE,CAChE,CAAC;YACJ,CAAC;YAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAEtE,IAAI,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC5C,MAAM,SAAS,CAAC;YAClB,CAAC;YAED,0BAA0B;YAC1B,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC;gBACtB,OAAO,EAAE,CAAC;gBACV,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAC5B,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,CAC3C,CAAC;gBACF,SAAS;YACX,CAAC;YAED,MAAM,SAAS,CAAC;QAClB,CAAC;IACH,CAAC;IAED,MAAM,SAAS,IAAI,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAwD,EAAE;IAE1D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,QAAQ,GAAG,YACf,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,EAChD,EAAE,CAAC;QAEH,OAAO,MAAM,UAAU,CAAW,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CACV,6BACE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,EAAE,CACH,CAAC;QACF,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAgB;IAEhB,IAAI,CAAC,CAAC,MAAM,eAAe,EAAE,CAAC,EAAE,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACvC,MAAM,YAAY,EAAE,CAAC;IACvB,CAAC;IAED,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QAClC,IAAI,WAAmC,CAAC;QACxC,IAAI,CAAC;YACH,WAAW,GAAG,MAAM,cAAc,EAAE,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;gBAClD,MAAM,YAAY,EAAE,CAAC;gBACrB,SAAS;YACX,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,aAAa,EAAE,GAAG,QAAQ,EAAE,EAAE;YACjE,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,WAAW,EAAE;SACxD,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;gBAClD,MAAM,UAAU,EAAE,CAAC;gBACnB,MAAM,YAAY,EAAE,CAAC;gBACrB,SAAS;YACX,CAAC;YACD,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;QACJ,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAGpD,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK,kBAAkB,EAAE,CAAC;gBACrD,MAAM,IAAI,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC9C,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,eAAe,GAAG,KAAK,CAAC;AAE5B,iDAAiD;AACjD,MAAM,UAAU,iBAAiB;IAC/B,eAAe,GAAG,KAAK,CAAC;AAC1B,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,mBAAmB;IACjC,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC;AAED,6GAA6G;AAC7G,MAAM,aAAa,GAAG,4BAA4B,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,gBAAgB,EAAE,EAAE,OAAO;QAAE,OAAO;IACxC,IAAI,eAAe;QAAE,OAAO;IAE5B,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,aAAa,CAAC,CAAC;IAClD,kEAAkE;IAClE,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,kCAAkC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAC3E,CAAC;IACJ,CAAC;IACD,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAoD,EACpD,IAAY,EACZ,QAAgB,EAChB,UAAgC,EAAE;IAElC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,WAAW,CAChC,eAAe,IAAI,IAAI,IAAI,SAAS,MAAM,CAAC,QAAQ,EAAE,EAAE,CACxD,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,wBAAwB,IAAI,KAAK,IAAI,IAAI,QAAQ,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAC/F,CAAC;QACJ,CAAC;QAED,eAAe,GAAG,IAAI,CAAC;QACvB,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,oBAAoB;YAAE,MAAM,KAAK,CAAC;QACvD,MAAM,CAAC,KAAK,CACV,mCACE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,EAAE,CACH,CAAC;QACF,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Licence errors
|
|
3
|
+
*
|
|
4
|
+
* Tailar has one lifetime licence. Anything that fetches registry files needs
|
|
5
|
+
* a logged-in licence holder; this is the single message for "no licence".
|
|
6
|
+
*/
|
|
7
|
+
export declare const BUY_URL = "https://tailar.dev/checkout";
|
|
8
|
+
export declare const LICENCE_REQUIRED_MESSAGE = "Tailar needs the lifetime licence ($99). Get it at https://tailar.dev/checkout, then run this command again.";
|
|
9
|
+
export declare class LicenceRequiredError extends Error {
|
|
10
|
+
readonly buyUrl: string;
|
|
11
|
+
constructor(buyUrl?: string);
|
|
12
|
+
}
|
|
13
|
+
export declare function isLicenceRequiredError(error: unknown): error is LicenceRequiredError;
|
|
14
|
+
/** Print a command failure and exit. A missing licence gets its own plain message. */
|
|
15
|
+
export declare function exitWithError(prefix: string, error: unknown): never;
|
|
16
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/api/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,eAAO,MAAM,OAAO,gCAAgC,CAAC;AAErD,eAAO,MAAM,wBAAwB,iHAAgG,CAAC;AAEtI,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,MAAM,GAAE,MAAgB;CAKrC;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAKpF;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,CAOnE"}
|