@hasna/shortlinks 0.2.7 → 0.2.10
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 +21 -24
- package/cloudflare/wrangler.example.toml +1 -1
- package/dist/cli/index.js +1602 -6178
- package/dist/cli/projection.d.ts +32 -0
- package/dist/client-store.d.ts +7 -7
- package/dist/cloud-store.d.ts +9 -7
- package/dist/generated/storage-kit/backend.d.ts +19 -0
- package/dist/generated/storage-kit/index.d.ts +2 -2
- package/dist/generated/storage-kit/own.d.ts +10 -0
- package/dist/generated/storage-kit/pool.d.ts +7 -6
- package/dist/generated/storage-kit/tls.d.ts +30 -3
- package/dist/index.js +1117 -6331
- package/dist/mcp/http.d.ts +2 -2
- package/dist/mcp/index.js +588 -6095
- package/dist/sdk/generated.d.ts +4 -4
- package/dist/sdk/index.d.ts +1 -2
- package/dist/sdk/index.js +13 -5
- package/dist/serve/app.d.ts +8 -5
- package/dist/serve/index.d.ts +5 -4
- package/dist/serve/index.js +762 -5251
- package/dist/store-interface.d.ts +4 -2
- package/infra/aws-ec2-user-data.sh +6 -6
- package/package.json +8 -5
- package/dist/generated/storage-kit/mode.d.ts +0 -47
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability-bearing URL projection.
|
|
3
|
+
*
|
|
4
|
+
* Incident 716957 (todos b03cc058): a stored destination that is itself a
|
|
5
|
+
* signed capability URL (an S3 presigned read URL, GCS, or CloudFront signed
|
|
6
|
+
* URL) was emitted verbatim by the CLI into a probe file and reproduced in a
|
|
7
|
+
* session transcript, granting bearer read access until expiry. `secrets scan`
|
|
8
|
+
* does not cover this class, so consumer-side containment is the interim
|
|
9
|
+
* behaviour required by the capability-bearing-output doctrine.
|
|
10
|
+
*
|
|
11
|
+
* Output therefore projects such a URL to its plain unsigned reference — the
|
|
12
|
+
* scheme/host/path with every capability query parameter stripped. A consumer
|
|
13
|
+
* resolving the projected reference goes through the normal unsigned path and
|
|
14
|
+
* never receives a bearer credential from a transcript.
|
|
15
|
+
*/
|
|
16
|
+
import type { Link } from "../types.js";
|
|
17
|
+
/** True when `url` is an http(s) URL whose query carries a signed-capability parameter. */
|
|
18
|
+
export declare function hasCapabilityQuery(url: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Project `url` to its plain unsigned reference: the same scheme/host/path
|
|
21
|
+
* with every capability query parameter stripped and non-capability parameters
|
|
22
|
+
* preserved. Non-capability or malformed values are returned unchanged.
|
|
23
|
+
*/
|
|
24
|
+
export declare function projectDestinationUrl(url: string): string;
|
|
25
|
+
/** Project a Link record's destination_url (identity when already plain). */
|
|
26
|
+
export declare function projectLink(link: Link): Link;
|
|
27
|
+
/**
|
|
28
|
+
* Project a CLI payload for output: a Link, an array of Links, or a LinkStats
|
|
29
|
+
* record get their destination_url projected; every other payload passes
|
|
30
|
+
* through untouched (same object reference).
|
|
31
|
+
*/
|
|
32
|
+
export declare function projectForOutput(data: unknown): unknown;
|
package/dist/client-store.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { CloudShortlinksStore } from "./cloud-store.js";
|
|
2
|
-
import type { Env } from "
|
|
3
|
-
import type { ListLinksOptions, Store, TotalStats } from "./store-interface.js";
|
|
2
|
+
import type { Env, ListLinksOptions, Store, TotalStats } from "./store-interface.js";
|
|
4
3
|
import type { AddDomainInput, Click, ClickInput, CreateLinkInput, Domain, Link, LinkStats } from "./types.js";
|
|
5
|
-
/** The
|
|
4
|
+
/** The hosted-API HTTP transport. */
|
|
6
5
|
export { CloudShortlinksStore as ApiStore } from "./cloud-store.js";
|
|
7
6
|
export type { Store } from "./store-interface.js";
|
|
8
7
|
/**
|
|
@@ -30,15 +29,16 @@ export declare class LocalStore implements Store {
|
|
|
30
29
|
close(): Promise<void>;
|
|
31
30
|
}
|
|
32
31
|
export interface ResolveStoreOptions {
|
|
33
|
-
/** Explicit local SQLite path (CLI `--db`); ignored
|
|
32
|
+
/** Explicit local SQLite path (CLI `--db`); ignored when the hosted API is selected. */
|
|
34
33
|
dbPath?: string;
|
|
35
|
-
/** Transport overrides for the
|
|
34
|
+
/** Transport overrides for the hosted-API client (test injection: fetchImpl, ...). */
|
|
36
35
|
cloudOverrides?: Parameters<typeof CloudShortlinksStore.fromEnv>[1];
|
|
37
36
|
}
|
|
38
37
|
/**
|
|
39
38
|
* Resolve the active {@link Store} for the current environment. Returns the
|
|
40
|
-
*
|
|
41
|
-
* {@link LocalStore}. Throws when
|
|
39
|
+
* hosted-API {@link ApiStore} when the client env is fully configured,
|
|
40
|
+
* otherwise a {@link LocalStore}. Throws when the hosted client is partially
|
|
41
|
+
* configured.
|
|
42
42
|
*/
|
|
43
43
|
export declare function resolveStore(env?: Env, options?: ResolveStoreOptions): Store;
|
|
44
44
|
/**
|
package/dist/cloud-store.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { resolveStorageClient, type HasnaStorageClient } from "@hasna/contracts/client/storage";
|
|
2
|
-
import type { Env } from "
|
|
3
|
-
import type { Store } from "./store-interface.js";
|
|
2
|
+
import type { Env, Store } from "./store-interface.js";
|
|
4
3
|
import type { AddDomainInput, Click, ClickInput, CreateLinkInput, Domain, Link, LinkStats } from "./types.js";
|
|
5
4
|
/**
|
|
6
5
|
* Cloud-backed shortlinks store. All methods hit `/v1` over HTTPS with the
|
|
@@ -9,16 +8,19 @@ import type { AddDomainInput, Click, ClickInput, CreateLinkInput, Domain, Link,
|
|
|
9
8
|
*/
|
|
10
9
|
export declare class CloudShortlinksStore implements Store {
|
|
11
10
|
private readonly client;
|
|
12
|
-
readonly kind: "
|
|
11
|
+
readonly kind: "http";
|
|
13
12
|
readonly transport: HasnaStorageClient["transport"];
|
|
14
13
|
private constructor();
|
|
15
14
|
/** Base `<origin>/v1` URL this store targets (for status/diagnostics). */
|
|
16
15
|
get baseUrl(): string;
|
|
17
16
|
/**
|
|
18
|
-
* Resolve a
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* Resolve a hosted-API store from the environment. The contracts client
|
|
18
|
+
* seam (`resolveStorageClient`) decides: an explicit API URL + API key in
|
|
19
|
+
* the environment selects the hosted client, and the fleet app-config on
|
|
20
|
+
* disk is the fallback tier; otherwise the caller uses its local store.
|
|
21
|
+
* Throws when only one of URL/key is set in the environment tier — a
|
|
22
|
+
* partially configured hosted client must fail loudly, never silently
|
|
23
|
+
* drift to the local dataset.
|
|
22
24
|
*/
|
|
23
25
|
static fromEnv(env?: Env, overrides?: Parameters<typeof resolveStorageClient>[2]): CloudShortlinksStore | null;
|
|
24
26
|
close(): Promise<void>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const SERVER_DATA_BACKENDS: readonly ["sqlite", "postgresql"];
|
|
2
|
+
export type ServerDataBackend = (typeof SERVER_DATA_BACKENDS)[number];
|
|
3
|
+
export type Env = Record<string, string | undefined>;
|
|
4
|
+
/** Upper-snake env token for an app name, e.g. `todos` -> `TODOS`. */
|
|
5
|
+
export declare function envToken(name: string): string;
|
|
6
|
+
export interface ServerDataBackendEnvKeys {
|
|
7
|
+
databaseUrlKeys: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function serverDataBackendEnvKeys(name: string): ServerDataBackendEnvKeys;
|
|
10
|
+
export declare function assertNoLegacyStorageMode(name: string, env?: Env): void;
|
|
11
|
+
export interface ServerDataBackendResolution {
|
|
12
|
+
backend: ServerDataBackend;
|
|
13
|
+
source: string;
|
|
14
|
+
databaseUrlPresent: boolean;
|
|
15
|
+
databaseUrlSource: string | null;
|
|
16
|
+
}
|
|
17
|
+
export declare function resolveServerDataBackend(name: string, env?: Env): ServerDataBackendResolution;
|
|
18
|
+
/** Resolve the database URL without logging it. Returns `null` when unset. */
|
|
19
|
+
export declare function resolveDatabaseUrl(name: string, env?: Env): string | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read `key` from `source` only when `source` OWNS it.
|
|
3
|
+
*
|
|
4
|
+
* Returns `undefined` for an inherited property and for a null/undefined or
|
|
5
|
+
* non-object source, so a guarded read is a drop-in for `source?.[key]` that
|
|
6
|
+
* cannot be answered by the prototype chain.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ownProp<T>(source: unknown, key: string): T | undefined;
|
|
9
|
+
/** `ownProp` narrowed to a string, so a polluted non-string cannot slip through. */
|
|
10
|
+
export declare function ownString(source: unknown, key: string): string | undefined;
|
|
@@ -14,20 +14,21 @@ export interface CreatePgPoolOptions extends TlsResolveOptions {
|
|
|
14
14
|
}
|
|
15
15
|
/** Build a `pg.Pool` with fleet-standard TLS handling. */
|
|
16
16
|
export declare function createPgPool(options: CreatePgPoolOptions): Pool;
|
|
17
|
-
export interface
|
|
17
|
+
export interface CreateServerPoolFromEnvOptions extends TlsResolveOptions {
|
|
18
18
|
max?: number;
|
|
19
19
|
idleTimeoutMillis?: number;
|
|
20
20
|
connectionTimeoutMillis?: number;
|
|
21
21
|
applicationName?: string;
|
|
22
22
|
}
|
|
23
|
-
export interface
|
|
23
|
+
export interface ServerPoolFromEnv {
|
|
24
24
|
client: PoolQueryClient;
|
|
25
25
|
connectionSource: string;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* Resolve
|
|
28
|
+
* Resolve backend + database URL from the environment and build the server's
|
|
29
|
+
* PostgreSQL pool.
|
|
29
30
|
*
|
|
30
|
-
* Throws when
|
|
31
|
-
*
|
|
31
|
+
* Throws when no database URL selects the `postgresql` backend. Never logs the
|
|
32
|
+
* URL.
|
|
32
33
|
*/
|
|
33
|
-
export declare function
|
|
34
|
+
export declare function createServerPoolFromEnv(appName: string, options?: CreateServerPoolFromEnvOptions): ServerPoolFromEnv;
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
export type PgSslConfig = boolean | {
|
|
3
3
|
rejectUnauthorized: boolean;
|
|
4
4
|
ca?: string;
|
|
5
|
+
cert?: string;
|
|
6
|
+
key?: string;
|
|
7
|
+
passphrase?: string;
|
|
5
8
|
};
|
|
6
9
|
export interface TlsResolveOptions {
|
|
7
10
|
/** Inline CA bundle (PEM). Wins over every other CA source. */
|
|
@@ -12,14 +15,38 @@ export interface TlsResolveOptions {
|
|
|
12
15
|
env?: Record<string, string | undefined>;
|
|
13
16
|
}
|
|
14
17
|
export type SslMode = "disable" | "prefer" | "require" | "verify-ca" | "verify-full";
|
|
18
|
+
/**
|
|
19
|
+
* Remove the TLS query parameters once this module has resolved them into an
|
|
20
|
+
* explicit `ssl` option. pg re-parses `connectionString` AFTER merging pool
|
|
21
|
+
* options and lets the parse win, so leaving `sslmode` (or `ssl`, `sslcert`,
|
|
22
|
+
* `sslkey`, `sslrootcert`, `sslnegotiation`) in the URL replaces the resolved
|
|
23
|
+
* SSL object with pg's own — discarding the CA bundle with it.
|
|
24
|
+
*
|
|
25
|
+
* Non-TLS parameters and any fragment are preserved exactly.
|
|
26
|
+
*/
|
|
27
|
+
export declare function connectionStringWithoutTlsParameters(connectionString: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Preserve pg's transport negotiation choice outside the stripped URL, so
|
|
30
|
+
* `sslnegotiation=direct` survives as an explicit pool option instead of being
|
|
31
|
+
* silently dropped.
|
|
32
|
+
*/
|
|
33
|
+
export declare function sslNegotiationFromConnectionString(connectionString: string): "postgres" | "direct" | undefined;
|
|
15
34
|
/**
|
|
16
35
|
* Extract the effective `sslmode` from a Postgres connection string. Honors the
|
|
17
|
-
* `sslmode` query param
|
|
18
|
-
*
|
|
36
|
+
* `sslmode` query param, the legacy `ssl=true` boolean, and pg's rule that
|
|
37
|
+
* `sslnegotiation=direct` implies TLS when no explicit SSL setting is present.
|
|
38
|
+
* Returns `disable` when TLS is not requested.
|
|
19
39
|
*/
|
|
20
40
|
export declare function sslModeFromConnectionString(connectionString: string): SslMode;
|
|
21
41
|
/**
|
|
22
42
|
* Resolve the `pg` ssl config for a connection string. See the module header
|
|
23
|
-
* for the full mode table.
|
|
43
|
+
* for the full mode table.
|
|
44
|
+
*
|
|
45
|
+
* Returns `false` when TLS was explicitly switched off, and `undefined` when the
|
|
46
|
+
* DSN expressed no TLS policy at all — those are different answers, and pg
|
|
47
|
+
* treats them differently: only `undefined` lets `PGSSLMODE` decide.
|
|
48
|
+
*
|
|
49
|
+
* The caller MUST hand pg `connectionStringWithoutTlsParameters(connectionString)`
|
|
50
|
+
* rather than the original DSN, or pg discards everything resolved here.
|
|
24
51
|
*/
|
|
25
52
|
export declare function resolveTlsConfig(connectionString: string, options?: TlsResolveOptions): PgSslConfig | undefined;
|