@agentic-kit/db-tools 0.11.5 → 0.12.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 +40 -1
- package/app-workspace.d.ts +29 -2
- package/app-workspace.js +99 -8
- package/blueprint-construction.d.ts +60 -0
- package/blueprint-construction.js +161 -0
- package/context.d.ts +5 -1
- package/context.js +30 -5
- package/db-probe.d.ts +25 -17
- package/db-probe.js +97 -37
- package/esm/app-workspace.d.ts +29 -2
- package/esm/app-workspace.js +96 -8
- package/esm/blueprint-construction.d.ts +60 -0
- package/esm/blueprint-construction.js +154 -0
- package/esm/context.d.ts +5 -1
- package/esm/context.js +31 -6
- package/esm/db-probe.d.ts +25 -17
- package/esm/db-probe.js +96 -37
- package/esm/graphql-http.d.ts +35 -0
- package/esm/graphql-http.js +134 -0
- package/esm/host.d.ts +46 -0
- package/esm/host.js +9 -0
- package/esm/index.d.ts +11 -2
- package/esm/index.js +6 -0
- package/esm/provision-database/env-file.d.ts +23 -2
- package/esm/provision-database/env-file.js +86 -27
- package/esm/provision-database/pending-ticket.d.ts +33 -0
- package/esm/provision-database/pending-ticket.js +63 -0
- package/esm/provision-database/provision.d.ts +71 -0
- package/esm/provision-database/provision.js +557 -0
- package/esm/provision-database/request-database.d.ts +55 -3
- package/esm/provision-database/request-database.js +118 -17
- package/esm/schema-model/normalize.d.ts +21 -0
- package/esm/schema-model/normalize.js +244 -0
- package/esm/schema-model.d.ts +110 -0
- package/esm/schema-model.js +303 -0
- package/esm/tools/add-policies.d.ts +1 -1
- package/esm/tools/add-records.d.ts +1 -0
- package/esm/tools/add-records.js +18 -6
- package/esm/tools/add-relation.js +2 -3
- package/esm/tools/create-api-key.d.ts +2 -0
- package/esm/tools/create-api-key.js +27 -12
- package/esm/tools/manage-entity-types.d.ts +1 -1
- package/esm/tools/provision-blueprint.d.ts +6 -0
- package/esm/tools/provision-blueprint.js +11 -4
- package/esm/tools/provision-database.d.ts +8 -11
- package/esm/tools/provision-database.js +23 -243
- package/esm/tools/run-codegen.d.ts +19 -2
- package/esm/tools/run-codegen.js +184 -97
- package/esm/tools/templates.js +3 -15
- package/graphql-http.d.ts +35 -0
- package/graphql-http.js +140 -0
- package/host.d.ts +46 -0
- package/host.js +10 -0
- package/index.d.ts +11 -2
- package/index.js +26 -1
- package/package.json +4 -4
- package/provision-database/env-file.d.ts +23 -2
- package/provision-database/env-file.js +89 -28
- package/provision-database/pending-ticket.d.ts +33 -0
- package/provision-database/pending-ticket.js +72 -0
- package/provision-database/provision.d.ts +71 -0
- package/provision-database/provision.js +565 -0
- package/provision-database/request-database.d.ts +55 -3
- package/provision-database/request-database.js +124 -18
- package/schema-model/normalize.d.ts +21 -0
- package/schema-model/normalize.js +250 -0
- package/schema-model.d.ts +110 -0
- package/schema-model.js +309 -0
- package/tools/add-policies.d.ts +1 -1
- package/tools/add-records.d.ts +1 -0
- package/tools/add-records.js +18 -6
- package/tools/add-relation.js +2 -3
- package/tools/create-api-key.d.ts +2 -0
- package/tools/create-api-key.js +27 -12
- package/tools/manage-entity-types.d.ts +1 -1
- package/tools/provision-blueprint.d.ts +6 -0
- package/tools/provision-blueprint.js +11 -4
- package/tools/provision-database.d.ts +8 -11
- package/tools/provision-database.js +23 -245
- package/tools/run-codegen.d.ts +19 -2
- package/tools/run-codegen.js +184 -96
- package/tools/templates.js +3 -15
package/esm/tools/templates.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { filterInternalPolicies } from '@agentic-kit/harness';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { buildBlueprint } from '../blueprint-construction';
|
|
3
4
|
import { resolveProjectContext } from '../context';
|
|
4
5
|
// A stored blueprint field's `type` is an expanded object (`{ name: 'text' }`),
|
|
5
6
|
// not the bare string the agent passes to provision_blueprint. Flatten it to the
|
|
@@ -252,21 +253,8 @@ export const applyTemplateTool = {
|
|
|
252
253
|
const blueprintId = copyResult.copyTemplateToBlueprint?.result;
|
|
253
254
|
if (!blueprintId)
|
|
254
255
|
throw new Error('Failed to copy template');
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
.unwrap();
|
|
258
|
-
const constructionId = constructResult.constructBlueprint?.result;
|
|
259
|
-
if (!constructionId)
|
|
260
|
-
throw new Error('constructBlueprint returned no construction id');
|
|
261
|
-
const constructions = await modules.blueprintConstruction
|
|
262
|
-
.findMany({
|
|
263
|
-
where: { id: { equalTo: constructionId } },
|
|
264
|
-
first: 1,
|
|
265
|
-
select: { tableMap: true },
|
|
266
|
-
})
|
|
267
|
-
.unwrap();
|
|
268
|
-
const tableMap = (constructions?.blueprintConstructions?.nodes?.[0]?.tableMap ?? {});
|
|
269
|
-
const createdCount = Object.keys(tableMap).length;
|
|
256
|
+
const { tables } = await buildBlueprint(modules, { blueprintId, schemaId }, { signal: ctx.signal });
|
|
257
|
+
const createdCount = Object.keys(tables).length;
|
|
270
258
|
return done(true, `Applied template "${params.templateName}" — created ${createdCount} table${createdCount === 1 ? '' : 's'}`);
|
|
271
259
|
}
|
|
272
260
|
catch (err) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type GraphQLAdapter, type GraphQLError } from '@constructive-io/graphql-query/runtime';
|
|
2
|
+
export type { GraphQLError };
|
|
3
|
+
export type HttpGraphQLResponse<T> = {
|
|
4
|
+
/** The response status, or null when no response arrived. */
|
|
5
|
+
httpStatus: number | null;
|
|
6
|
+
data: T | null;
|
|
7
|
+
/**
|
|
8
|
+
* Body errors, each with `extensions.httpStatus` added. A non-2xx without
|
|
9
|
+
* body errors becomes one `{ code: 'HTTP_ERROR', httpStatus }` error, and a
|
|
10
|
+
* fetch that threw becomes one `{ code: 'NETWORK' }` error.
|
|
11
|
+
*/
|
|
12
|
+
errors: GraphQLError[];
|
|
13
|
+
};
|
|
14
|
+
export type PostGraphQLArgs = {
|
|
15
|
+
endpoint: string;
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
query: string;
|
|
18
|
+
variables?: Record<string, unknown>;
|
|
19
|
+
fetchFn?: typeof fetch;
|
|
20
|
+
};
|
|
21
|
+
export declare function postGraphQL<T>(args: PostGraphQLArgs): Promise<HttpGraphQLResponse<T>>;
|
|
22
|
+
/**
|
|
23
|
+
* An ORM adapter over `postGraphQL`: the SDK clients take it in place of their
|
|
24
|
+
* FetchAdapter, and their failures then carry the status and code.
|
|
25
|
+
*/
|
|
26
|
+
export declare function statusAdapter(endpoint: string, headers?: Record<string, string>, fetchFn?: typeof fetch): GraphQLAdapter;
|
|
27
|
+
export type BackendErrorKind = 'suspended' | 'auth' | 'forbidden' | 'server' | 'network' | 'graphql';
|
|
28
|
+
/**
|
|
29
|
+
* What a failed backend answer says, first match wins: the database is
|
|
30
|
+
* suspended or gone (`ACCESS_SUSPENDED`), no response, the backend failing
|
|
31
|
+
* (5xx, 429), a rejected credential (401), a refusal (403), or anything else.
|
|
32
|
+
*/
|
|
33
|
+
export declare function classifyBackendErrors(errors: readonly GraphQLError[]): BackendErrorKind;
|
|
34
|
+
/** The messages of a failed answer, joined for a reason string. */
|
|
35
|
+
export declare function errorsText(errors: readonly GraphQLError[]): string;
|
package/graphql-http.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// A GraphQL POST that keeps what the SDK's FetchAdapter throws away. That
|
|
3
|
+
// adapter turns any non-2xx into `HTTP 403: Forbidden` and drops the body, so
|
|
4
|
+
// a tenant API's `ACCESS_SUSPENDED` (a 403 with a JSON body, sent for a
|
|
5
|
+
// deleted or suspended database) reads the same as any other refusal. Here
|
|
6
|
+
// the body's errors keep their `extensions.code`, and every error carries the
|
|
7
|
+
// HTTP status it arrived with.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.postGraphQL = postGraphQL;
|
|
10
|
+
exports.statusAdapter = statusAdapter;
|
|
11
|
+
exports.classifyBackendErrors = classifyBackendErrors;
|
|
12
|
+
exports.errorsText = errorsText;
|
|
13
|
+
const runtime_1 = require("@constructive-io/graphql-query/runtime");
|
|
14
|
+
const errorText = (err) => (err instanceof Error ? err.message : String(err));
|
|
15
|
+
async function postGraphQL(args) {
|
|
16
|
+
const fetchFn = (args.fetchFn ?? (0, runtime_1.createFetch)()).bind(globalThis);
|
|
17
|
+
let response;
|
|
18
|
+
try {
|
|
19
|
+
response = await fetchFn(args.endpoint, {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
Accept: 'application/json',
|
|
24
|
+
...args.headers,
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({ query: args.query, variables: args.variables ?? {} }),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
const cause = err?.cause?.code;
|
|
31
|
+
return {
|
|
32
|
+
httpStatus: null,
|
|
33
|
+
data: null,
|
|
34
|
+
errors: [
|
|
35
|
+
{
|
|
36
|
+
message: typeof cause === 'string' ? `${errorText(err)} (${cause})` : errorText(err),
|
|
37
|
+
extensions: { code: 'NETWORK' },
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const httpStatus = response.status;
|
|
43
|
+
let body = null;
|
|
44
|
+
try {
|
|
45
|
+
body = (await response.json());
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
body = null;
|
|
49
|
+
}
|
|
50
|
+
const bodyErrors = Array.isArray(body?.errors) ? body.errors : [];
|
|
51
|
+
if (bodyErrors.length > 0) {
|
|
52
|
+
return {
|
|
53
|
+
httpStatus,
|
|
54
|
+
data: body?.data ?? null,
|
|
55
|
+
errors: bodyErrors.map((error) => ({
|
|
56
|
+
...error,
|
|
57
|
+
message: error?.message ?? '',
|
|
58
|
+
extensions: { ...error?.extensions, httpStatus },
|
|
59
|
+
})),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (!response.ok || !body) {
|
|
63
|
+
const text = response.statusText ? `: ${response.statusText}` : '';
|
|
64
|
+
return {
|
|
65
|
+
httpStatus,
|
|
66
|
+
data: null,
|
|
67
|
+
errors: [
|
|
68
|
+
{
|
|
69
|
+
message: response.ok ? `HTTP ${httpStatus}${text} (the body was not JSON)` : `HTTP ${httpStatus}${text}`,
|
|
70
|
+
extensions: { code: 'HTTP_ERROR', httpStatus },
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { httpStatus, data: body.data ?? null, errors: [] };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* An ORM adapter over `postGraphQL`: the SDK clients take it in place of their
|
|
79
|
+
* FetchAdapter, and their failures then carry the status and code.
|
|
80
|
+
*/
|
|
81
|
+
function statusAdapter(endpoint, headers, fetchFn) {
|
|
82
|
+
let current = { ...headers };
|
|
83
|
+
return {
|
|
84
|
+
async execute(document, variables) {
|
|
85
|
+
const result = await postGraphQL({ endpoint, headers: current, query: document, variables, fetchFn });
|
|
86
|
+
return result.errors.length > 0
|
|
87
|
+
? { ok: false, data: null, errors: result.errors }
|
|
88
|
+
: { ok: true, data: result.data, errors: undefined };
|
|
89
|
+
},
|
|
90
|
+
setHeaders(next) {
|
|
91
|
+
current = { ...current, ...next };
|
|
92
|
+
},
|
|
93
|
+
getEndpoint: () => endpoint,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const codeOf = (error) => {
|
|
97
|
+
const code = error?.extensions?.code;
|
|
98
|
+
return typeof code === 'string' ? code : undefined;
|
|
99
|
+
};
|
|
100
|
+
// The status an error arrived with: ours from extensions, or the SDK
|
|
101
|
+
// adapter's `HTTP 503: …` message.
|
|
102
|
+
const statusOf = (error) => {
|
|
103
|
+
const status = error?.extensions?.httpStatus;
|
|
104
|
+
if (typeof status === 'number')
|
|
105
|
+
return status;
|
|
106
|
+
const match = /^HTTP (\d{3})\b/.exec(error?.message ?? '');
|
|
107
|
+
return match ? Number(match[1]) : undefined;
|
|
108
|
+
};
|
|
109
|
+
const AUTH_MESSAGE_RE = /jwt|unauthenticated|invalid token/i;
|
|
110
|
+
const FORBIDDEN_MESSAGE_RE = /permission denied/i;
|
|
111
|
+
/**
|
|
112
|
+
* What a failed backend answer says, first match wins: the database is
|
|
113
|
+
* suspended or gone (`ACCESS_SUSPENDED`), no response, the backend failing
|
|
114
|
+
* (5xx, 429), a rejected credential (401), a refusal (403), or anything else.
|
|
115
|
+
*/
|
|
116
|
+
function classifyBackendErrors(errors) {
|
|
117
|
+
const list = errors ?? [];
|
|
118
|
+
const some = (test) => list.some((error) => test(error));
|
|
119
|
+
if (some((e) => codeOf(e) === 'ACCESS_SUSPENDED'))
|
|
120
|
+
return 'suspended';
|
|
121
|
+
if (some((e) => codeOf(e) === 'NETWORK'))
|
|
122
|
+
return 'network';
|
|
123
|
+
if (some((e) => {
|
|
124
|
+
const status = statusOf(e);
|
|
125
|
+
return status !== undefined && (status >= 500 || status === 429);
|
|
126
|
+
})) {
|
|
127
|
+
return 'server';
|
|
128
|
+
}
|
|
129
|
+
if (some((e) => statusOf(e) === 401 || codeOf(e) === 'UNAUTHENTICATED' || AUTH_MESSAGE_RE.test(e?.message ?? ''))) {
|
|
130
|
+
return 'auth';
|
|
131
|
+
}
|
|
132
|
+
if (some((e) => statusOf(e) === 403 || codeOf(e) === 'FORBIDDEN' || FORBIDDEN_MESSAGE_RE.test(e?.message ?? ''))) {
|
|
133
|
+
return 'forbidden';
|
|
134
|
+
}
|
|
135
|
+
return 'graphql';
|
|
136
|
+
}
|
|
137
|
+
/** The messages of a failed answer, joined for a reason string. */
|
|
138
|
+
function errorsText(errors) {
|
|
139
|
+
return (errors ?? []).map((error) => error?.message || codeOf(error) || 'unknown error').join('; ');
|
|
140
|
+
}
|
package/host.d.ts
CHANGED
|
@@ -74,6 +74,23 @@ export type StepUpRequest = {
|
|
|
74
74
|
databaseName: string;
|
|
75
75
|
apiEndpoint: string;
|
|
76
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* The database a project is bound to, handed to the host before provisioning
|
|
79
|
+
* writes it to the project's `.env`, so a host that keeps its own record of
|
|
80
|
+
* the binding never ends up with a `.env` it doesn't know about.
|
|
81
|
+
*/
|
|
82
|
+
export type ProjectBinding = {
|
|
83
|
+
cwd: string;
|
|
84
|
+
databaseId: string;
|
|
85
|
+
databaseName: string;
|
|
86
|
+
ownerId: string;
|
|
87
|
+
apiEndpoint: string;
|
|
88
|
+
modulesEndpoint: string;
|
|
89
|
+
/** The database this binding replaced (replace mode). */
|
|
90
|
+
replaces?: string;
|
|
91
|
+
/** True when the existing binding was kept: nothing was provisioned. */
|
|
92
|
+
skipped?: boolean;
|
|
93
|
+
};
|
|
77
94
|
export interface ToolsHost {
|
|
78
95
|
/** Signed-in platform account, or null/undefined when signed out. */
|
|
79
96
|
account(): HostAccount | null | undefined;
|
|
@@ -91,6 +108,13 @@ export interface ToolsHost {
|
|
|
91
108
|
previewToken?(): Promise<PreviewToken | null>;
|
|
92
109
|
/** Treat tokens expiring within this window as already expired. Default 30s. */
|
|
93
110
|
dataTokenSkewMs?: number;
|
|
111
|
+
/**
|
|
112
|
+
* How long provision_database waits for a provision ticket to complete.
|
|
113
|
+
* Default 240s, which keeps an agent tool call under a 5-minute tool
|
|
114
|
+
* timeout; a host that calls the tool outside such a timeout (e.g. a
|
|
115
|
+
* pipeline) can wait longer for a cold provision on a loaded backend.
|
|
116
|
+
*/
|
|
117
|
+
provisionPollDeadlineMs?: number;
|
|
94
118
|
/**
|
|
95
119
|
* Optional provision overlay: pick a base preset and/or layer module
|
|
96
120
|
* add/remove on top of it. The base module list always comes from the pinned
|
|
@@ -109,7 +133,29 @@ export interface ToolsHost {
|
|
|
109
133
|
* Required for create_api_key — without it the tool refuses to mint.
|
|
110
134
|
*/
|
|
111
135
|
deliverSecret?(delivery: SecretDelivery): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* What to tell the agent to do about a database that is gone or belongs to
|
|
138
|
+
* another account, in place of the default advice to call
|
|
139
|
+
* provision_database with `reprovision: true`.
|
|
140
|
+
*/
|
|
141
|
+
reprovisionHint?: string;
|
|
142
|
+
/**
|
|
143
|
+
* Whether the agent may replace a binding through provision_database's
|
|
144
|
+
* `reprovision: true`. Default true. A host that replaces databases only
|
|
145
|
+
* after the user approves (calling `provisionDatabase` in replace mode
|
|
146
|
+
* itself) sets false, and the tool then refuses.
|
|
147
|
+
*/
|
|
148
|
+
toolReprovision?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Save a binding in the host's own store. Provisioning calls it before
|
|
151
|
+
* writing `.env`, on a new binding and on a kept one (`skipped`). If it
|
|
152
|
+
* throws, `.env` is left alone, the provision ticket stays recorded, and
|
|
153
|
+
* the call fails with `binding-not-saved`.
|
|
154
|
+
*/
|
|
155
|
+
persistBinding?(binding: ProjectBinding): Promise<void>;
|
|
112
156
|
}
|
|
113
157
|
export declare const DEFAULT_DATA_TOKEN_SKEW_MS = 30000;
|
|
158
|
+
/** The advice for a database that is gone or under another account. */
|
|
159
|
+
export declare function reprovisionAdvice(host: ToolsHost): string;
|
|
114
160
|
export declare function configureHost(host: ToolsHost): void;
|
|
115
161
|
export declare function getHost(): ToolsHost;
|
package/host.js
CHANGED
|
@@ -13,9 +13,19 @@
|
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.DEFAULT_DATA_TOKEN_SKEW_MS = void 0;
|
|
16
|
+
exports.reprovisionAdvice = reprovisionAdvice;
|
|
16
17
|
exports.configureHost = configureHost;
|
|
17
18
|
exports.getHost = getHost;
|
|
18
19
|
exports.DEFAULT_DATA_TOKEN_SKEW_MS = 30_000;
|
|
20
|
+
const DEFAULT_REPROVISION_ADVICE = 'Re-provision with provision_database (reprovision: true): the schema is rebuilt from the project, and records do not carry over.';
|
|
21
|
+
/** The advice for a database that is gone or under another account. */
|
|
22
|
+
function reprovisionAdvice(host) {
|
|
23
|
+
if (host.reprovisionHint)
|
|
24
|
+
return host.reprovisionHint;
|
|
25
|
+
if (host.toolReprovision === false)
|
|
26
|
+
return 'Ask the user to re-provision it from the app.';
|
|
27
|
+
return DEFAULT_REPROVISION_ADVICE;
|
|
28
|
+
}
|
|
19
29
|
let currentHost = null;
|
|
20
30
|
function configureHost(host) {
|
|
21
31
|
currentHost = host;
|
package/index.d.ts
CHANGED
|
@@ -10,12 +10,21 @@ import { type ToolsHost } from './host';
|
|
|
10
10
|
export declare const constructiveDbTools: readonly AnyHarnessTool[];
|
|
11
11
|
/** Configure the host and get the tools in one call. */
|
|
12
12
|
export declare function createConstructiveDbTools(host: ToolsHost): readonly AnyHarnessTool[];
|
|
13
|
-
export {
|
|
13
|
+
export { BlueprintBuildError, buildBlueprint, CONSTRUCTION_DEADLINE_MS, type ConstructionTableMap, readConstructionFailure, waitForConstruction, type WaitForConstructionOptions, } from './blueprint-construction';
|
|
14
|
+
export { CONTEXT_ENV_KEYS, CONTEXT_ENV_PREFIX, type ContextEnvKey, type ContextSource, deriveSubdomainEndpoint, fromEnvFile, fromEnvironment, type ModulesClient, type ProjectContext, type ProjectContextFailureCode, resolveDataToken, resolveProjectContext, type ResolveResult, } from './context';
|
|
15
|
+
export { type DatabaseProbe, probeBoundDatabase, probeDatabase, type UnreachableCause, } from './db-probe';
|
|
14
16
|
export { constructiveGateDeps, type ConstructiveGateResolvers } from './gate';
|
|
15
|
-
export { type
|
|
17
|
+
export { type BackendErrorKind, classifyBackendErrors, type HttpGraphQLResponse, postGraphQL, statusAdapter, } from './graphql-http';
|
|
18
|
+
export { type ActiveDataToken, configureHost, type DataAuthBroker, DEFAULT_DATA_TOKEN_SKEW_MS, getHost, type HostAccount, type HostBackendConfig, type HostProvisionOverlay, type PreviewToken, type ProjectBinding, type SecretDelivery, type StepUpRequest, type ToolsHost, } from './host';
|
|
19
|
+
export { archiveBindingKeys, ARCHIVED_BINDING_KEYS, type EnvVars, mergeEnv, provisionEnvVars, readEnv, } from './provision-database/env-file';
|
|
16
20
|
export { loadProvisionManifest, parseProvisionManifest, PROVISION_MANIFEST_FILE, type ProvisionManifest, } from './provision-database/manifest';
|
|
17
21
|
export { allModulePresets, DEFAULT_PROVISION_PRESET, getModulePreset, type ModulePreset, type ProvisionModule, } from './provision-database/presets';
|
|
22
|
+
export { provisionDatabase, type ProvisionDatabaseDetails, type ProvisionFailureCode, type ProvisionInput, } from './provision-database/provision';
|
|
18
23
|
export { moduleKey, type ProvisionOverlay, resolveProvisionModules, } from './provision-database/resolve';
|
|
24
|
+
export { accessOf, type ColumnDefault, type FkAction, type ModelColumn, type ModelForeignKey, type ModelPolicy, type ModelTable, type ObjectCategory, readSchemaModel, type SchemaModel, SchemaReadError, type TableAccess, } from './schema-model';
|
|
19
25
|
export { toolSchema } from './tool-schema';
|
|
26
|
+
export { type AddRecordsDetails } from './tools/add-records';
|
|
27
|
+
export { type CreateApiKeyDetails } from './tools/create-api-key';
|
|
28
|
+
export { type RunCodegenDetails, type RunCodegenTimings } from './tools/run-codegen';
|
|
20
29
|
export { createTemplatePreviewTables } from './tools/templates';
|
|
21
30
|
export default constructiveDbTools;
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createTemplatePreviewTables = exports.toolSchema = exports.resolveProvisionModules = exports.moduleKey = exports.getModulePreset = exports.DEFAULT_PROVISION_PRESET = exports.allModulePresets = exports.PROVISION_MANIFEST_FILE = exports.parseProvisionManifest = exports.loadProvisionManifest = exports.getHost = exports.DEFAULT_DATA_TOKEN_SKEW_MS = exports.configureHost = exports.constructiveGateDeps = exports.resolveProjectContext = exports.resolveDataToken = exports.fromEnvironment = exports.fromEnvFile = exports.deriveSubdomainEndpoint = exports.CONTEXT_ENV_PREFIX = exports.CONTEXT_ENV_KEYS = exports.constructiveDbTools = void 0;
|
|
3
|
+
exports.createTemplatePreviewTables = exports.toolSchema = exports.SchemaReadError = exports.readSchemaModel = exports.accessOf = exports.resolveProvisionModules = exports.moduleKey = exports.provisionDatabase = exports.getModulePreset = exports.DEFAULT_PROVISION_PRESET = exports.allModulePresets = exports.PROVISION_MANIFEST_FILE = exports.parseProvisionManifest = exports.loadProvisionManifest = exports.readEnv = exports.provisionEnvVars = exports.mergeEnv = exports.ARCHIVED_BINDING_KEYS = exports.archiveBindingKeys = exports.getHost = exports.DEFAULT_DATA_TOKEN_SKEW_MS = exports.configureHost = exports.statusAdapter = exports.postGraphQL = exports.classifyBackendErrors = exports.constructiveGateDeps = exports.probeDatabase = exports.probeBoundDatabase = exports.resolveProjectContext = exports.resolveDataToken = exports.fromEnvironment = exports.fromEnvFile = exports.deriveSubdomainEndpoint = exports.CONTEXT_ENV_PREFIX = exports.CONTEXT_ENV_KEYS = exports.waitForConstruction = exports.readConstructionFailure = exports.CONSTRUCTION_DEADLINE_MS = exports.buildBlueprint = exports.BlueprintBuildError = exports.constructiveDbTools = void 0;
|
|
4
4
|
exports.createConstructiveDbTools = createConstructiveDbTools;
|
|
5
5
|
const host_1 = require("./host");
|
|
6
6
|
const add_policies_1 = require("./tools/add-policies");
|
|
@@ -46,6 +46,12 @@ function createConstructiveDbTools(host) {
|
|
|
46
46
|
(0, host_1.configureHost)(host);
|
|
47
47
|
return exports.constructiveDbTools;
|
|
48
48
|
}
|
|
49
|
+
var blueprint_construction_1 = require("./blueprint-construction");
|
|
50
|
+
Object.defineProperty(exports, "BlueprintBuildError", { enumerable: true, get: function () { return blueprint_construction_1.BlueprintBuildError; } });
|
|
51
|
+
Object.defineProperty(exports, "buildBlueprint", { enumerable: true, get: function () { return blueprint_construction_1.buildBlueprint; } });
|
|
52
|
+
Object.defineProperty(exports, "CONSTRUCTION_DEADLINE_MS", { enumerable: true, get: function () { return blueprint_construction_1.CONSTRUCTION_DEADLINE_MS; } });
|
|
53
|
+
Object.defineProperty(exports, "readConstructionFailure", { enumerable: true, get: function () { return blueprint_construction_1.readConstructionFailure; } });
|
|
54
|
+
Object.defineProperty(exports, "waitForConstruction", { enumerable: true, get: function () { return blueprint_construction_1.waitForConstruction; } });
|
|
49
55
|
var context_1 = require("./context");
|
|
50
56
|
Object.defineProperty(exports, "CONTEXT_ENV_KEYS", { enumerable: true, get: function () { return context_1.CONTEXT_ENV_KEYS; } });
|
|
51
57
|
Object.defineProperty(exports, "CONTEXT_ENV_PREFIX", { enumerable: true, get: function () { return context_1.CONTEXT_ENV_PREFIX; } });
|
|
@@ -54,12 +60,25 @@ Object.defineProperty(exports, "fromEnvFile", { enumerable: true, get: function
|
|
|
54
60
|
Object.defineProperty(exports, "fromEnvironment", { enumerable: true, get: function () { return context_1.fromEnvironment; } });
|
|
55
61
|
Object.defineProperty(exports, "resolveDataToken", { enumerable: true, get: function () { return context_1.resolveDataToken; } });
|
|
56
62
|
Object.defineProperty(exports, "resolveProjectContext", { enumerable: true, get: function () { return context_1.resolveProjectContext; } });
|
|
63
|
+
var db_probe_1 = require("./db-probe");
|
|
64
|
+
Object.defineProperty(exports, "probeBoundDatabase", { enumerable: true, get: function () { return db_probe_1.probeBoundDatabase; } });
|
|
65
|
+
Object.defineProperty(exports, "probeDatabase", { enumerable: true, get: function () { return db_probe_1.probeDatabase; } });
|
|
57
66
|
var gate_1 = require("./gate");
|
|
58
67
|
Object.defineProperty(exports, "constructiveGateDeps", { enumerable: true, get: function () { return gate_1.constructiveGateDeps; } });
|
|
68
|
+
var graphql_http_1 = require("./graphql-http");
|
|
69
|
+
Object.defineProperty(exports, "classifyBackendErrors", { enumerable: true, get: function () { return graphql_http_1.classifyBackendErrors; } });
|
|
70
|
+
Object.defineProperty(exports, "postGraphQL", { enumerable: true, get: function () { return graphql_http_1.postGraphQL; } });
|
|
71
|
+
Object.defineProperty(exports, "statusAdapter", { enumerable: true, get: function () { return graphql_http_1.statusAdapter; } });
|
|
59
72
|
var host_2 = require("./host");
|
|
60
73
|
Object.defineProperty(exports, "configureHost", { enumerable: true, get: function () { return host_2.configureHost; } });
|
|
61
74
|
Object.defineProperty(exports, "DEFAULT_DATA_TOKEN_SKEW_MS", { enumerable: true, get: function () { return host_2.DEFAULT_DATA_TOKEN_SKEW_MS; } });
|
|
62
75
|
Object.defineProperty(exports, "getHost", { enumerable: true, get: function () { return host_2.getHost; } });
|
|
76
|
+
var env_file_1 = require("./provision-database/env-file");
|
|
77
|
+
Object.defineProperty(exports, "archiveBindingKeys", { enumerable: true, get: function () { return env_file_1.archiveBindingKeys; } });
|
|
78
|
+
Object.defineProperty(exports, "ARCHIVED_BINDING_KEYS", { enumerable: true, get: function () { return env_file_1.ARCHIVED_BINDING_KEYS; } });
|
|
79
|
+
Object.defineProperty(exports, "mergeEnv", { enumerable: true, get: function () { return env_file_1.mergeEnv; } });
|
|
80
|
+
Object.defineProperty(exports, "provisionEnvVars", { enumerable: true, get: function () { return env_file_1.provisionEnvVars; } });
|
|
81
|
+
Object.defineProperty(exports, "readEnv", { enumerable: true, get: function () { return env_file_1.readEnv; } });
|
|
63
82
|
var manifest_1 = require("./provision-database/manifest");
|
|
64
83
|
Object.defineProperty(exports, "loadProvisionManifest", { enumerable: true, get: function () { return manifest_1.loadProvisionManifest; } });
|
|
65
84
|
Object.defineProperty(exports, "parseProvisionManifest", { enumerable: true, get: function () { return manifest_1.parseProvisionManifest; } });
|
|
@@ -68,9 +87,15 @@ var presets_1 = require("./provision-database/presets");
|
|
|
68
87
|
Object.defineProperty(exports, "allModulePresets", { enumerable: true, get: function () { return presets_1.allModulePresets; } });
|
|
69
88
|
Object.defineProperty(exports, "DEFAULT_PROVISION_PRESET", { enumerable: true, get: function () { return presets_1.DEFAULT_PROVISION_PRESET; } });
|
|
70
89
|
Object.defineProperty(exports, "getModulePreset", { enumerable: true, get: function () { return presets_1.getModulePreset; } });
|
|
90
|
+
var provision_1 = require("./provision-database/provision");
|
|
91
|
+
Object.defineProperty(exports, "provisionDatabase", { enumerable: true, get: function () { return provision_1.provisionDatabase; } });
|
|
71
92
|
var resolve_1 = require("./provision-database/resolve");
|
|
72
93
|
Object.defineProperty(exports, "moduleKey", { enumerable: true, get: function () { return resolve_1.moduleKey; } });
|
|
73
94
|
Object.defineProperty(exports, "resolveProvisionModules", { enumerable: true, get: function () { return resolve_1.resolveProvisionModules; } });
|
|
95
|
+
var schema_model_1 = require("./schema-model");
|
|
96
|
+
Object.defineProperty(exports, "accessOf", { enumerable: true, get: function () { return schema_model_1.accessOf; } });
|
|
97
|
+
Object.defineProperty(exports, "readSchemaModel", { enumerable: true, get: function () { return schema_model_1.readSchemaModel; } });
|
|
98
|
+
Object.defineProperty(exports, "SchemaReadError", { enumerable: true, get: function () { return schema_model_1.SchemaReadError; } });
|
|
74
99
|
var tool_schema_1 = require("./tool-schema");
|
|
75
100
|
Object.defineProperty(exports, "toolSchema", { enumerable: true, get: function () { return tool_schema_1.toolSchema; } });
|
|
76
101
|
var templates_2 = require("./tools/templates");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentic-kit/db-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "harness-neutral Constructive database tools — provisioning, schema, policies, records, templates and codegen as HarnessTools any adapter can bind",
|
|
6
6
|
"main": "index.js",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"12factor-env": "^1.34.0",
|
|
33
33
|
"@agentic-kit/harness": "^0.18.0",
|
|
34
|
-
"@constructive-io/graphql-query": "^4.22.
|
|
35
|
-
"@constructive-io/sdk": "^1.21.
|
|
34
|
+
"@constructive-io/graphql-query": "^4.22.6",
|
|
35
|
+
"@constructive-io/sdk": "^1.21.6",
|
|
36
36
|
"node-type-registry": "^1.22.0",
|
|
37
37
|
"zod": "^4.4.3"
|
|
38
38
|
},
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"database",
|
|
46
46
|
"constructive"
|
|
47
47
|
],
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "124df300f0afc15653854b50a25ac738c70261b7"
|
|
49
49
|
}
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
export type EnvVars = Record<string, string>;
|
|
2
|
+
/** Parse .env text: quoted values, `export` prefixes and comments; last definition wins. */
|
|
3
|
+
export declare function readEnv(content: string): EnvVars;
|
|
2
4
|
/**
|
|
3
|
-
* Upsert `vars` into the existing `.env` `content`.
|
|
4
|
-
* in place (
|
|
5
|
+
* Upsert `vars` into the existing `.env` `content`. An existing key is
|
|
6
|
+
* replaced in place (keeping an `export` prefix), and any later definition of
|
|
7
|
+
* it is dropped so the new value is the one that wins; new keys are appended.
|
|
5
8
|
* Returns the new file body, trailing-newline-terminated.
|
|
6
9
|
*/
|
|
7
10
|
export declare function mergeEnv(content: string, vars: EnvVars): string;
|
|
11
|
+
/**
|
|
12
|
+
* Drop every definition of `keys`. Comments and other entries pass through,
|
|
13
|
+
* and content without those keys comes back unchanged.
|
|
14
|
+
*/
|
|
15
|
+
export declare function removeEnvKeys(content: string, keys: readonly string[]): string;
|
|
16
|
+
/**
|
|
17
|
+
* The provision request provision_database is still waiting on, recorded when
|
|
18
|
+
* it stops waiting so the next call resumes that ticket. It's resumed only
|
|
19
|
+
* for the same database name, backend and account. Absent whenever no request
|
|
20
|
+
* is pending.
|
|
21
|
+
*/
|
|
22
|
+
export declare const PENDING_PROVISION_KEYS: {
|
|
23
|
+
readonly ticketId: "PROVISION_TICKET_ID";
|
|
24
|
+
readonly databaseName: "PROVISION_TICKET_DATABASE_NAME";
|
|
25
|
+
readonly apiEndpoint: "PROVISION_TICKET_API_ENDPOINT";
|
|
26
|
+
readonly userId: "PROVISION_TICKET_USER_ID";
|
|
27
|
+
readonly attempts: "PROVISION_TICKET_ATTEMPTS";
|
|
28
|
+
};
|
|
8
29
|
/** The credential keys provision_database owns in a project's .env. */
|
|
9
30
|
export declare const PROVISION_ENV_KEYS: readonly ["DATABASE_ID", "DATABASE_NAME", "OWNER_ID", "ACCESS_TOKEN", "PGDATABASE", "API_ENDPOINT", "MODULES_ENDPOINT"];
|
|
10
31
|
/**
|
|
@@ -1,32 +1,100 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Pure .env
|
|
3
|
-
// credentials without clobbering existing keys
|
|
4
|
-
// present, else append.
|
|
2
|
+
// Pure .env edits — used by provision_database to persist provisioning
|
|
3
|
+
// credentials without clobbering existing keys.
|
|
5
4
|
// Kept side-effect-free so it is unit-testable (the tool does the fs write).
|
|
5
|
+
//
|
|
6
|
+
// Reads go through the same parser context.ts uses (quotes, `export`,
|
|
7
|
+
// comments, the last definition wins), and edits find a key the way that
|
|
8
|
+
// parser does, so a key written as `export KEY="value"` or spread over several
|
|
9
|
+
// lines by a quoted value is still the key being replaced or removed.
|
|
6
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.ARCHIVED_BINDING_KEYS = exports.PROVISION_ENV_KEYS = void 0;
|
|
11
|
+
exports.ARCHIVED_BINDING_KEYS = exports.PROVISION_ENV_KEYS = exports.PENDING_PROVISION_KEYS = void 0;
|
|
12
|
+
exports.readEnv = readEnv;
|
|
8
13
|
exports.mergeEnv = mergeEnv;
|
|
14
|
+
exports.removeEnvKeys = removeEnvKeys;
|
|
9
15
|
exports.provisionEnvVars = provisionEnvVars;
|
|
10
16
|
exports.archiveBindingKeys = archiveBindingKeys;
|
|
17
|
+
const dotenv_1 = require("12factor-env/dotenv");
|
|
18
|
+
/** Parse .env text: quoted values, `export` prefixes and comments; last definition wins. */
|
|
19
|
+
function readEnv(content) {
|
|
20
|
+
return (0, dotenv_1.parseDotenv)(content);
|
|
21
|
+
}
|
|
22
|
+
const ENTRY_RE = /^(\s*)(export\s+)?([\w.-]+)\s*=\s*(.*)$/;
|
|
23
|
+
const QUOTES = new Set(['"', "'", '`']);
|
|
24
|
+
// Group lines into entries: a key with the lines its value spans (a quoted
|
|
25
|
+
// value left open continues to the line that closes it), or a non-entry line
|
|
26
|
+
// (comment, blank, anything else) with a null key.
|
|
27
|
+
function envEntries(content) {
|
|
28
|
+
const lines = content.split('\n');
|
|
29
|
+
const entries = [];
|
|
30
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31
|
+
const line = lines[i];
|
|
32
|
+
const match = line.trimStart().startsWith('#') ? null : ENTRY_RE.exec(line);
|
|
33
|
+
if (!match) {
|
|
34
|
+
entries.push({ key: null, exported: false, lines: [line] });
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const value = match[4];
|
|
38
|
+
const span = [line];
|
|
39
|
+
if (QUOTES.has(value[0]) && !value.slice(1).includes(value[0])) {
|
|
40
|
+
while (i + 1 < lines.length) {
|
|
41
|
+
span.push(lines[++i]);
|
|
42
|
+
if (lines[i].includes(value[0]))
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
entries.push({ key: match[3], exported: !!match[2], lines: span });
|
|
47
|
+
}
|
|
48
|
+
return entries;
|
|
49
|
+
}
|
|
11
50
|
/**
|
|
12
|
-
* Upsert `vars` into the existing `.env` `content`.
|
|
13
|
-
* in place (
|
|
51
|
+
* Upsert `vars` into the existing `.env` `content`. An existing key is
|
|
52
|
+
* replaced in place (keeping an `export` prefix), and any later definition of
|
|
53
|
+
* it is dropped so the new value is the one that wins; new keys are appended.
|
|
14
54
|
* Returns the new file body, trailing-newline-terminated.
|
|
15
55
|
*/
|
|
16
56
|
function mergeEnv(content, vars) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
57
|
+
const remaining = new Map(Object.entries(vars));
|
|
58
|
+
const written = new Set();
|
|
59
|
+
const out = [];
|
|
60
|
+
for (const entry of envEntries(content)) {
|
|
61
|
+
if (entry.key !== null && remaining.has(entry.key)) {
|
|
62
|
+
out.push(`${entry.exported ? 'export ' : ''}${entry.key}=${remaining.get(entry.key)}`);
|
|
63
|
+
remaining.delete(entry.key);
|
|
64
|
+
written.add(entry.key);
|
|
65
|
+
}
|
|
66
|
+
else if (entry.key === null || !written.has(entry.key)) {
|
|
67
|
+
out.push(...entry.lines);
|
|
68
|
+
}
|
|
27
69
|
}
|
|
28
|
-
|
|
70
|
+
let text = out.join('\n');
|
|
71
|
+
for (const [key, value] of remaining)
|
|
72
|
+
text += `\n${key}=${value}`;
|
|
73
|
+
return text.trim() + '\n';
|
|
29
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Drop every definition of `keys`. Comments and other entries pass through,
|
|
77
|
+
* and content without those keys comes back unchanged.
|
|
78
|
+
*/
|
|
79
|
+
function removeEnvKeys(content, keys) {
|
|
80
|
+
const keySet = new Set(keys);
|
|
81
|
+
const entries = envEntries(content);
|
|
82
|
+
const kept = entries.filter((entry) => entry.key === null || !keySet.has(entry.key));
|
|
83
|
+
return kept.length === entries.length ? content : kept.flatMap((entry) => entry.lines).join('\n');
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The provision request provision_database is still waiting on, recorded when
|
|
87
|
+
* it stops waiting so the next call resumes that ticket. It's resumed only
|
|
88
|
+
* for the same database name, backend and account. Absent whenever no request
|
|
89
|
+
* is pending.
|
|
90
|
+
*/
|
|
91
|
+
exports.PENDING_PROVISION_KEYS = {
|
|
92
|
+
ticketId: 'PROVISION_TICKET_ID',
|
|
93
|
+
databaseName: 'PROVISION_TICKET_DATABASE_NAME',
|
|
94
|
+
apiEndpoint: 'PROVISION_TICKET_API_ENDPOINT',
|
|
95
|
+
userId: 'PROVISION_TICKET_USER_ID',
|
|
96
|
+
attempts: 'PROVISION_TICKET_ATTEMPTS',
|
|
97
|
+
};
|
|
30
98
|
/** The credential keys provision_database owns in a project's .env. */
|
|
31
99
|
exports.PROVISION_ENV_KEYS = [
|
|
32
100
|
'DATABASE_ID',
|
|
@@ -76,16 +144,9 @@ function provisionEnvVars(args) {
|
|
|
76
144
|
*/
|
|
77
145
|
function archiveBindingKeys(content, keys, archivedOn) {
|
|
78
146
|
const keySet = new Set(keys);
|
|
79
|
-
return content
|
|
80
|
-
.
|
|
81
|
-
.map((line) => {
|
|
82
|
-
|
|
83
|
-
return line;
|
|
84
|
-
const eq = line.indexOf('=');
|
|
85
|
-
if (eq === -1)
|
|
86
|
-
return line;
|
|
87
|
-
const key = line.slice(0, eq).trim();
|
|
88
|
-
return keySet.has(key) ? `# archived ${archivedOn}: ${line.trim()}` : line;
|
|
89
|
-
})
|
|
147
|
+
return envEntries(content)
|
|
148
|
+
.flatMap((entry) => entry.key !== null && keySet.has(entry.key)
|
|
149
|
+
? entry.lines.map((line) => `# archived ${archivedOn}: ${line.trim()}`)
|
|
150
|
+
: entry.lines)
|
|
90
151
|
.join('\n');
|
|
91
152
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type EnvVars } from './env-file';
|
|
2
|
+
/**
|
|
3
|
+
* Pending results recorded for one ticket before provision_database stops
|
|
4
|
+
* following it. At the default 240 s deadline that is about 20 minutes.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MAX_PENDING_ATTEMPTS = 5;
|
|
7
|
+
export type PendingTicket = {
|
|
8
|
+
ticketId: string;
|
|
9
|
+
databaseName: string;
|
|
10
|
+
apiEndpoint: string;
|
|
11
|
+
userId: string;
|
|
12
|
+
/** Pending results recorded for this ticket so far. */
|
|
13
|
+
attempts: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Tickets the host told provisioning to stop following (`abandonPending`),
|
|
17
|
+
* comma-separated. A lookup by name skips them, so an abandoned provision is
|
|
18
|
+
* never resumed.
|
|
19
|
+
*/
|
|
20
|
+
export declare const ABANDONED_TICKETS_KEY = "PROVISION_TICKET_ABANDONED";
|
|
21
|
+
/**
|
|
22
|
+
* The recorded ticket, with the name, backend and account it was requested
|
|
23
|
+
* for. Only a ticket from this backend and account is ours to poll, and one
|
|
24
|
+
* for another name is a different database.
|
|
25
|
+
*/
|
|
26
|
+
export declare function recordedTicket(env: EnvVars): PendingTicket | undefined;
|
|
27
|
+
export declare function recordPendingTicket(content: string, ticket: PendingTicket): string;
|
|
28
|
+
export declare function clearPendingTicket(content: string): string;
|
|
29
|
+
/** Comment the record out (`# archived <date>: …`), keeping it readable. */
|
|
30
|
+
export declare function archivePendingTicket(content: string, archivedOn: string): string;
|
|
31
|
+
export declare function abandonedTickets(env: EnvVars): Set<string>;
|
|
32
|
+
/** Add ticket ids to the abandoned list. */
|
|
33
|
+
export declare function recordAbandoned(content: string, ticketIds: readonly string[]): string;
|