@shipstatic/types 2.25.0 → 2.26.0-beta.1
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/dist/index.d.ts +1 -1
- package/dist/schemas.js +1 -1
- package/dist/time.d.ts +50 -0
- package/dist/time.js +66 -0
- package/package.json +8 -1
- package/src/index.ts +1 -1
- package/src/schemas.ts +1 -1
- package/src/time.ts +69 -0
package/dist/index.d.ts
CHANGED
|
@@ -135,7 +135,7 @@ export interface Deployment {
|
|
|
135
135
|
readonly created: number;
|
|
136
136
|
/** Unix timestamp (seconds) when deployment expires, null if never */
|
|
137
137
|
expires: number | null;
|
|
138
|
-
/** Full URL to the deployment screenshot (e.g., 'https://screenshots.shipstatic.com/happy-cat-abc1234/a3f2c1b4d5e6f789') */
|
|
138
|
+
/** Full URL to the deployment screenshot, rendered on the first request for it (e.g., 'https://screenshots.shipstatic.com/happy-cat-abc1234/a3f2c1b4d5e6f789') */
|
|
139
139
|
readonly screenshot: string;
|
|
140
140
|
}
|
|
141
141
|
/**
|
package/dist/schemas.js
CHANGED
|
@@ -75,7 +75,7 @@ export const DeploymentSchema = z.object({
|
|
|
75
75
|
.describe(`Unix timestamp (seconds) when the deployment expires; null when permanent. Anonymous deployments expire ${PUBLIC_DEPLOYMENT_TTL_SECONDS / 86_400} days after creation unless claimed; an authenticated deployment carries one only when it requested a ttl.`),
|
|
76
76
|
screenshot: z
|
|
77
77
|
.url()
|
|
78
|
-
.describe('Full URL to the deployment screenshot.
|
|
78
|
+
.describe('Full URL to the deployment screenshot. Rendered on the first request for it, so the URL is returned immediately and the first request takes a few seconds; every request after that is served immediately.'),
|
|
79
79
|
});
|
|
80
80
|
export const DeploymentCreateResponseSchema = DeploymentSchema.extend({
|
|
81
81
|
claim: z
|
package/dist/time.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long a deployment has left, in the words a person reads.
|
|
3
|
+
*
|
|
4
|
+
* When this module was written (2026-09-17) one deadline was told four ways:
|
|
5
|
+
* the deploy card counted from now, the CLI and the marketing site from the
|
|
6
|
+
* deployment's creation, the VS Code palette quoted the anonymous tier's fixed
|
|
7
|
+
* lifetime, and the dashboard truncated, so a deployment made a second ago
|
|
8
|
+
* read "2d" there and "3 days" everywhere else. The rule since: a surface that
|
|
9
|
+
* tells a person how long a deployment has left says it with
|
|
10
|
+
* `formatTimeRemaining`, and a duration a person reads about deployments is
|
|
11
|
+
* spelled by `formatDuration`.
|
|
12
|
+
*
|
|
13
|
+
* The WORDS are the surface's own. A card says "Expires in", the marketing
|
|
14
|
+
* site says "It stays live for", and each says something different once the
|
|
15
|
+
* deadline has passed, which is why that case is `null` rather than a
|
|
16
|
+
* sentence. What is owned here is the part that drifted silently: the unit,
|
|
17
|
+
* the rounding, and what the clock is measured from.
|
|
18
|
+
*
|
|
19
|
+
* A subpath export (`@shipstatic/types/time`), for the same reason as
|
|
20
|
+
* `/schemas`: a browser bundle importing only this carries only this. From the
|
|
21
|
+
* main entry it would carry about a kilobyte of unrelated constants that a
|
|
22
|
+
* bundler cannot prove are unused (1 KB gzipped, measured with the deploy
|
|
23
|
+
* card's esbuild on 2026-09-17).
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* A span of time in words: `"3 days"`, `"5 hours"`, `"45 minutes"`.
|
|
27
|
+
*
|
|
28
|
+
* Minutes under two hours, hours under two days, and days beyond, each rounded
|
|
29
|
+
* to the nearest. The unit changes exactly where the larger one rounds to two,
|
|
30
|
+
* so the number never jumps as time passes (119 minutes, then 2 hours), and
|
|
31
|
+
* the two vaguest things a rounded clock can say, "1 hour" and "1 day", are
|
|
32
|
+
* never said. Rounding rather than truncating is what lets a deployment made a
|
|
33
|
+
* second ago read the lifetime it was given: "3 days", not "2 days". Days are
|
|
34
|
+
* the largest unit because nobody counts a deadline in weeks, and a month has
|
|
35
|
+
* no fixed length.
|
|
36
|
+
*
|
|
37
|
+
* A span that rounds to no minutes at all, zero and negative included, reads
|
|
38
|
+
* "less than a minute".
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatDuration(seconds: number): string;
|
|
41
|
+
/**
|
|
42
|
+
* The time left before a deadline, in words (`"3 days"`), or `null` once it
|
|
43
|
+
* has passed.
|
|
44
|
+
*
|
|
45
|
+
* `expires` is unix SECONDS, the wire's own field, taken as it arrives so that
|
|
46
|
+
* no caller converts it: the deploy card once read it as a date string and
|
|
47
|
+
* showed the same three days on every deployment for months. `now` is seconds
|
|
48
|
+
* too, and defaults to the clock.
|
|
49
|
+
*/
|
|
50
|
+
export declare function formatTimeRemaining(expires: number, now?: number): string | null;
|
package/dist/time.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long a deployment has left, in the words a person reads.
|
|
3
|
+
*
|
|
4
|
+
* When this module was written (2026-09-17) one deadline was told four ways:
|
|
5
|
+
* the deploy card counted from now, the CLI and the marketing site from the
|
|
6
|
+
* deployment's creation, the VS Code palette quoted the anonymous tier's fixed
|
|
7
|
+
* lifetime, and the dashboard truncated, so a deployment made a second ago
|
|
8
|
+
* read "2d" there and "3 days" everywhere else. The rule since: a surface that
|
|
9
|
+
* tells a person how long a deployment has left says it with
|
|
10
|
+
* `formatTimeRemaining`, and a duration a person reads about deployments is
|
|
11
|
+
* spelled by `formatDuration`.
|
|
12
|
+
*
|
|
13
|
+
* The WORDS are the surface's own. A card says "Expires in", the marketing
|
|
14
|
+
* site says "It stays live for", and each says something different once the
|
|
15
|
+
* deadline has passed, which is why that case is `null` rather than a
|
|
16
|
+
* sentence. What is owned here is the part that drifted silently: the unit,
|
|
17
|
+
* the rounding, and what the clock is measured from.
|
|
18
|
+
*
|
|
19
|
+
* A subpath export (`@shipstatic/types/time`), for the same reason as
|
|
20
|
+
* `/schemas`: a browser bundle importing only this carries only this. From the
|
|
21
|
+
* main entry it would carry about a kilobyte of unrelated constants that a
|
|
22
|
+
* bundler cannot prove are unused (1 KB gzipped, measured with the deploy
|
|
23
|
+
* card's esbuild on 2026-09-17).
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* A span of time in words: `"3 days"`, `"5 hours"`, `"45 minutes"`.
|
|
27
|
+
*
|
|
28
|
+
* Minutes under two hours, hours under two days, and days beyond, each rounded
|
|
29
|
+
* to the nearest. The unit changes exactly where the larger one rounds to two,
|
|
30
|
+
* so the number never jumps as time passes (119 minutes, then 2 hours), and
|
|
31
|
+
* the two vaguest things a rounded clock can say, "1 hour" and "1 day", are
|
|
32
|
+
* never said. Rounding rather than truncating is what lets a deployment made a
|
|
33
|
+
* second ago read the lifetime it was given: "3 days", not "2 days". Days are
|
|
34
|
+
* the largest unit because nobody counts a deadline in weeks, and a month has
|
|
35
|
+
* no fixed length.
|
|
36
|
+
*
|
|
37
|
+
* A span that rounds to no minutes at all, zero and negative included, reads
|
|
38
|
+
* "less than a minute".
|
|
39
|
+
*/
|
|
40
|
+
export function formatDuration(seconds) {
|
|
41
|
+
const minutes = Math.round(seconds / 60);
|
|
42
|
+
if (!(minutes >= 1))
|
|
43
|
+
return 'less than a minute';
|
|
44
|
+
if (minutes < 120)
|
|
45
|
+
return countOf(minutes, 'minute');
|
|
46
|
+
const hours = Math.round(seconds / 3_600);
|
|
47
|
+
if (hours < 48)
|
|
48
|
+
return countOf(hours, 'hour');
|
|
49
|
+
return countOf(Math.round(seconds / 86_400), 'day');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The time left before a deadline, in words (`"3 days"`), or `null` once it
|
|
53
|
+
* has passed.
|
|
54
|
+
*
|
|
55
|
+
* `expires` is unix SECONDS, the wire's own field, taken as it arrives so that
|
|
56
|
+
* no caller converts it: the deploy card once read it as a date string and
|
|
57
|
+
* showed the same three days on every deployment for months. `now` is seconds
|
|
58
|
+
* too, and defaults to the clock.
|
|
59
|
+
*/
|
|
60
|
+
export function formatTimeRemaining(expires, now = Date.now() / 1_000) {
|
|
61
|
+
const seconds = expires - now;
|
|
62
|
+
return seconds > 0 ? formatDuration(seconds) : null;
|
|
63
|
+
}
|
|
64
|
+
function countOf(count, unit) {
|
|
65
|
+
return `${count} ${unit}${count === 1 ? '' : 's'}`;
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipstatic/types",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0-beta.1",
|
|
4
4
|
"description": "Shared TypeScript types for the ShipStatic platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,12 +13,19 @@
|
|
|
13
13
|
"./schemas": {
|
|
14
14
|
"types": "./dist/schemas.d.ts",
|
|
15
15
|
"default": "./dist/schemas.js"
|
|
16
|
+
},
|
|
17
|
+
"./time": {
|
|
18
|
+
"types": "./dist/time.d.ts",
|
|
19
|
+
"default": "./dist/time.js"
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
22
|
"typesVersions": {
|
|
19
23
|
"*": {
|
|
20
24
|
"schemas": [
|
|
21
25
|
"./dist/schemas.d.ts"
|
|
26
|
+
],
|
|
27
|
+
"time": [
|
|
28
|
+
"./dist/time.d.ts"
|
|
22
29
|
]
|
|
23
30
|
}
|
|
24
31
|
},
|
package/src/index.ts
CHANGED
|
@@ -144,7 +144,7 @@ export interface Deployment {
|
|
|
144
144
|
readonly created: number;
|
|
145
145
|
/** Unix timestamp (seconds) when deployment expires, null if never */
|
|
146
146
|
expires: number | null; // Mutable - can be updated
|
|
147
|
-
/** Full URL to the deployment screenshot (e.g., 'https://screenshots.shipstatic.com/happy-cat-abc1234/a3f2c1b4d5e6f789') */
|
|
147
|
+
/** Full URL to the deployment screenshot, rendered on the first request for it (e.g., 'https://screenshots.shipstatic.com/happy-cat-abc1234/a3f2c1b4d5e6f789') */
|
|
148
148
|
readonly screenshot: string;
|
|
149
149
|
}
|
|
150
150
|
|
package/src/schemas.ts
CHANGED
|
@@ -94,7 +94,7 @@ export const DeploymentSchema = z.object({
|
|
|
94
94
|
screenshot: z
|
|
95
95
|
.url()
|
|
96
96
|
.describe(
|
|
97
|
-
'Full URL to the deployment screenshot.
|
|
97
|
+
'Full URL to the deployment screenshot. Rendered on the first request for it, so the URL is returned immediately and the first request takes a few seconds; every request after that is served immediately.',
|
|
98
98
|
),
|
|
99
99
|
});
|
|
100
100
|
|
package/src/time.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long a deployment has left, in the words a person reads.
|
|
3
|
+
*
|
|
4
|
+
* When this module was written (2026-09-17) one deadline was told four ways:
|
|
5
|
+
* the deploy card counted from now, the CLI and the marketing site from the
|
|
6
|
+
* deployment's creation, the VS Code palette quoted the anonymous tier's fixed
|
|
7
|
+
* lifetime, and the dashboard truncated, so a deployment made a second ago
|
|
8
|
+
* read "2d" there and "3 days" everywhere else. The rule since: a surface that
|
|
9
|
+
* tells a person how long a deployment has left says it with
|
|
10
|
+
* `formatTimeRemaining`, and a duration a person reads about deployments is
|
|
11
|
+
* spelled by `formatDuration`.
|
|
12
|
+
*
|
|
13
|
+
* The WORDS are the surface's own. A card says "Expires in", the marketing
|
|
14
|
+
* site says "It stays live for", and each says something different once the
|
|
15
|
+
* deadline has passed, which is why that case is `null` rather than a
|
|
16
|
+
* sentence. What is owned here is the part that drifted silently: the unit,
|
|
17
|
+
* the rounding, and what the clock is measured from.
|
|
18
|
+
*
|
|
19
|
+
* A subpath export (`@shipstatic/types/time`), for the same reason as
|
|
20
|
+
* `/schemas`: a browser bundle importing only this carries only this. From the
|
|
21
|
+
* main entry it would carry about a kilobyte of unrelated constants that a
|
|
22
|
+
* bundler cannot prove are unused (1 KB gzipped, measured with the deploy
|
|
23
|
+
* card's esbuild on 2026-09-17).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A span of time in words: `"3 days"`, `"5 hours"`, `"45 minutes"`.
|
|
28
|
+
*
|
|
29
|
+
* Minutes under two hours, hours under two days, and days beyond, each rounded
|
|
30
|
+
* to the nearest. The unit changes exactly where the larger one rounds to two,
|
|
31
|
+
* so the number never jumps as time passes (119 minutes, then 2 hours), and
|
|
32
|
+
* the two vaguest things a rounded clock can say, "1 hour" and "1 day", are
|
|
33
|
+
* never said. Rounding rather than truncating is what lets a deployment made a
|
|
34
|
+
* second ago read the lifetime it was given: "3 days", not "2 days". Days are
|
|
35
|
+
* the largest unit because nobody counts a deadline in weeks, and a month has
|
|
36
|
+
* no fixed length.
|
|
37
|
+
*
|
|
38
|
+
* A span that rounds to no minutes at all, zero and negative included, reads
|
|
39
|
+
* "less than a minute".
|
|
40
|
+
*/
|
|
41
|
+
export function formatDuration(seconds: number): string {
|
|
42
|
+
const minutes = Math.round(seconds / 60);
|
|
43
|
+
if (!(minutes >= 1)) return 'less than a minute';
|
|
44
|
+
if (minutes < 120) return countOf(minutes, 'minute');
|
|
45
|
+
const hours = Math.round(seconds / 3_600);
|
|
46
|
+
if (hours < 48) return countOf(hours, 'hour');
|
|
47
|
+
return countOf(Math.round(seconds / 86_400), 'day');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The time left before a deadline, in words (`"3 days"`), or `null` once it
|
|
52
|
+
* has passed.
|
|
53
|
+
*
|
|
54
|
+
* `expires` is unix SECONDS, the wire's own field, taken as it arrives so that
|
|
55
|
+
* no caller converts it: the deploy card once read it as a date string and
|
|
56
|
+
* showed the same three days on every deployment for months. `now` is seconds
|
|
57
|
+
* too, and defaults to the clock.
|
|
58
|
+
*/
|
|
59
|
+
export function formatTimeRemaining(
|
|
60
|
+
expires: number,
|
|
61
|
+
now: number = Date.now() / 1_000,
|
|
62
|
+
): string | null {
|
|
63
|
+
const seconds = expires - now;
|
|
64
|
+
return seconds > 0 ? formatDuration(seconds) : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function countOf(count: number, unit: string): string {
|
|
68
|
+
return `${count} ${unit}${count === 1 ? '' : 's'}`;
|
|
69
|
+
}
|