@mdbase-dev/pickle 0.1.0-beta.21

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mdbase connect contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # `@mdbase-dev/pickle`
2
+
3
+ Contract adapter and provisioning resources for Pickle collections.
4
+
5
+ ```ts
6
+ const pickle = new PickleCollection(connect);
7
+ const requests = await pickle.list();
8
+ await pickle.respond(requests[0], {
9
+ decision: "approve",
10
+ comment: "Looks right."
11
+ });
12
+ ```
13
+
14
+ The adapter uses ordinary mdbase operations. Request state is derived from
15
+ linked response records, and no record payload is stored outside the collection.
@@ -0,0 +1,98 @@
1
+ import type { CollectionContractDescriptor, CollectionDescription, CollectionTypeDescriptor, JsonObject, RecordDocument } from "@mdbase-dev/connect-protocol";
2
+ import type { MdbaseConnection, QueryResult } from "@mdbase-dev/connect";
3
+ export { PICKLE_ACK_RESPONSE_TYPE_DOCUMENT, PICKLE_APPROVAL_RESPONSE_TYPE_DOCUMENT, PICKLE_REQUEST_CONTRACT_DOCUMENT, PICKLE_TYPE_PACK_PROVISION, PICKLE_REQUEST_TYPE_DOCUMENT } from "./resources.js";
4
+ export declare const PICKLE_REQUEST_CONTRACT = "pickle.request";
5
+ export declare const PICKLE_REQUEST_CONTRACT_VERSION = "1.0.0";
6
+ export declare const PICKLE_NOTIFICATION_CRITERION = "pickle.request.created";
7
+ export type PickleRequestState = "pending" | "answered" | "conflict" | "cancelled";
8
+ export interface PickleFrontmatter extends JsonObject {
9
+ type?: string;
10
+ id?: string;
11
+ title?: string;
12
+ source?: string;
13
+ message?: string;
14
+ kind?: string;
15
+ status?: string;
16
+ priority?: string;
17
+ response_type?: string;
18
+ created_at?: string;
19
+ due_at?: string;
20
+ dedupe_key?: string;
21
+ tags?: unknown[];
22
+ links?: unknown[];
23
+ attachment_paths?: unknown[];
24
+ metadata?: JsonObject;
25
+ }
26
+ export interface PickleContractImplementation {
27
+ typeName: string;
28
+ fields: Record<string, string>;
29
+ binding?: JsonObject;
30
+ requestType: CollectionTypeDescriptor;
31
+ }
32
+ export interface PickleContract {
33
+ descriptor: CollectionContractDescriptor;
34
+ implementations: PickleContractImplementation[];
35
+ }
36
+ export interface PickleLink {
37
+ label: string;
38
+ url?: string;
39
+ path?: string;
40
+ }
41
+ export interface PickleAttachment {
42
+ path: string;
43
+ filename: string;
44
+ }
45
+ export interface PickleResponse {
46
+ path: string;
47
+ type: string;
48
+ responder: string;
49
+ respondedAt?: string;
50
+ payload: JsonObject;
51
+ frontmatter: JsonObject;
52
+ }
53
+ export interface PickleRequest {
54
+ id: string;
55
+ path: string;
56
+ title: string;
57
+ source: string;
58
+ message: string;
59
+ body: string;
60
+ kind: string;
61
+ priority: string;
62
+ status?: string;
63
+ state: PickleRequestState;
64
+ responseCount: number;
65
+ responseType: string;
66
+ responseTypeDefinition?: CollectionTypeDescriptor;
67
+ createdAt?: string;
68
+ dueAt?: string;
69
+ tags: string[];
70
+ links: PickleLink[];
71
+ attachments: PickleAttachment[];
72
+ metadata: JsonObject;
73
+ response?: PickleResponse;
74
+ frontmatter: PickleFrontmatter;
75
+ }
76
+ export interface RespondOptions {
77
+ responder?: string;
78
+ }
79
+ export interface PickleClient {
80
+ describe(): Promise<CollectionDescription>;
81
+ queryAll(input: Parameters<MdbaseConnection<PickleFrontmatter>["queryAll"]>[0]): Promise<QueryResult<PickleFrontmatter>>;
82
+ create(input: Parameters<MdbaseConnection<PickleFrontmatter>["create"]>[0]): ReturnType<MdbaseConnection<PickleFrontmatter>["create"]>;
83
+ }
84
+ export declare function resolvePickleContract(description: CollectionDescription): PickleContract;
85
+ export declare class PickleCollection {
86
+ private readonly connect;
87
+ private description;
88
+ private contract;
89
+ constructor(connect: PickleClient);
90
+ describe(): Promise<{
91
+ collection: CollectionDescription;
92
+ contract: PickleContract;
93
+ }>;
94
+ list(): Promise<PickleRequest[]>;
95
+ respond(request: PickleRequest, payload: JsonObject, options?: RespondOptions): Promise<RecordDocument<PickleFrontmatter>>;
96
+ }
97
+ export declare class PickleContractError extends Error {
98
+ }
package/dist/index.js ADDED
@@ -0,0 +1,268 @@
1
+ export { PICKLE_ACK_RESPONSE_TYPE_DOCUMENT, PICKLE_APPROVAL_RESPONSE_TYPE_DOCUMENT, PICKLE_REQUEST_CONTRACT_DOCUMENT, PICKLE_TYPE_PACK_PROVISION, PICKLE_REQUEST_TYPE_DOCUMENT } from "./resources.js";
2
+ export const PICKLE_REQUEST_CONTRACT = "pickle.request";
3
+ export const PICKLE_REQUEST_CONTRACT_VERSION = "1.0.0";
4
+ export const PICKLE_NOTIFICATION_CRITERION = "pickle.request.created";
5
+ const SYSTEM_RESPONSE_FIELDS = new Set([
6
+ "type",
7
+ "types",
8
+ "id",
9
+ "request",
10
+ "responded_at",
11
+ "responder",
12
+ "attachment_paths"
13
+ ]);
14
+ export function resolvePickleContract(description) {
15
+ const descriptor = description.contracts.find((contract) => contract.id === PICKLE_REQUEST_CONTRACT &&
16
+ contract.version === PICKLE_REQUEST_CONTRACT_VERSION);
17
+ if (!descriptor) {
18
+ throw new PickleContractError("This collection does not provide the Pickle request contract.");
19
+ }
20
+ const implementations = descriptor.implementations.map((implementation) => {
21
+ const requestType = description.types.find((candidate) => candidate.name === implementation.type_name);
22
+ if (!requestType)
23
+ throw new PickleContractError(`The Pickle request type ${implementation.type_name} is missing from this collection.`);
24
+ return {
25
+ typeName: implementation.type_name,
26
+ fields: parseFields(implementation.fields),
27
+ binding: implementation.binding,
28
+ requestType
29
+ };
30
+ });
31
+ if (!implementations.length)
32
+ throw new PickleContractError("The Pickle request contract has no type implementations.");
33
+ return {
34
+ descriptor,
35
+ implementations
36
+ };
37
+ }
38
+ export class PickleCollection {
39
+ connect;
40
+ description = null;
41
+ contract = null;
42
+ constructor(connect) {
43
+ this.connect = connect;
44
+ }
45
+ async describe() {
46
+ this.description ??= await this.connect.describe();
47
+ this.contract ??= resolvePickleContract(this.description);
48
+ return { collection: this.description, contract: this.contract };
49
+ }
50
+ async list() {
51
+ const { collection, contract } = await this.describe();
52
+ const requestQuery = await this.connect.queryAll({
53
+ types: contract.implementations.map(({ typeName }) => typeName),
54
+ include_body: true,
55
+ frontmatter_mode: "effective"
56
+ });
57
+ const requests = requestQuery.results.map(requireEffectiveFrontmatter);
58
+ const responseTypes = [
59
+ ...new Set(requests
60
+ .map((record) => {
61
+ const implementation = implementationForRecord(contract, record);
62
+ return stringField(record.effective_frontmatter, role(implementation, "response_type", "response_type"));
63
+ })
64
+ .filter(Boolean))
65
+ ];
66
+ const responses = responseTypes.length
67
+ ? (await this.connect.queryAll({
68
+ types: responseTypes,
69
+ include_body: true,
70
+ frontmatter_mode: "effective"
71
+ })).results.map(requireEffectiveFrontmatter)
72
+ : [];
73
+ return requests
74
+ .map((record) => normalizeRequest(record, responses, collection, contract))
75
+ .sort((left, right) => (right.createdAt ?? "").localeCompare(left.createdAt ?? ""));
76
+ }
77
+ async respond(request, payload, options = {}) {
78
+ if (request.state !== "pending") {
79
+ throw new PickleContractError(request.state === "conflict"
80
+ ? "Resolve the conflicting responses in the collection first."
81
+ : "This request already has a response.");
82
+ }
83
+ const responseType = request.responseTypeDefinition;
84
+ if (!responseType) {
85
+ throw new PickleContractError(`The response type ${request.responseType} is unavailable.`);
86
+ }
87
+ const responseFolder = stringField(asObject(responseType.collection?.path), "folder") ||
88
+ "responses";
89
+ const frontmatter = {
90
+ ...payload,
91
+ type: request.responseType,
92
+ request: requestLink(request.path),
93
+ responder: options.responder?.trim() || "human"
94
+ };
95
+ const created = await this.connect.create({
96
+ type: request.responseType,
97
+ path: `${trimSlashes(responseFolder)}/${crypto.randomUUID()}.md`,
98
+ frontmatter,
99
+ body: ""
100
+ });
101
+ assertValid(created);
102
+ return created.result;
103
+ }
104
+ }
105
+ export class PickleContractError extends Error {
106
+ }
107
+ function normalizeRequest(record, responseRecords, description, contract) {
108
+ const implementation = implementationForRecord(contract, record);
109
+ const responseType = stringField(record.effective_frontmatter, role(implementation, "response_type", "response_type")) || "pickle_response_approval";
110
+ const linked = responseRecords.filter((candidate) => linkTargets(candidate.effective_frontmatter.request, record.path));
111
+ const status = stringField(record.effective_frontmatter, role(implementation, "status", "status"));
112
+ const state = status === "cancelled"
113
+ ? "cancelled"
114
+ : linked.length === 0
115
+ ? "pending"
116
+ : linked.length === 1
117
+ ? "answered"
118
+ : "conflict";
119
+ const response = linked.length === 1 ? normalizeResponse(linked[0]) : undefined;
120
+ return {
121
+ id: stringField(record.effective_frontmatter, role(implementation, "id", "id")) ||
122
+ record.path,
123
+ path: record.path,
124
+ title: stringField(record.effective_frontmatter, role(implementation, "title", "title")) ||
125
+ record.path,
126
+ source: stringField(record.effective_frontmatter, role(implementation, "source", "source")) ||
127
+ "agent",
128
+ message: stringField(record.effective_frontmatter, role(implementation, "message", "message")),
129
+ body: record.body ?? "",
130
+ kind: stringField(record.effective_frontmatter, role(implementation, "kind", "kind")) ||
131
+ "approval",
132
+ priority: stringField(record.effective_frontmatter, role(implementation, "priority", "priority")) || "normal",
133
+ status: status || undefined,
134
+ state,
135
+ responseCount: linked.length,
136
+ responseType,
137
+ responseTypeDefinition: description.types.find((candidate) => candidate.name === responseType),
138
+ createdAt: stringField(record.effective_frontmatter, role(implementation, "created_at", "created_at")) || undefined,
139
+ dueAt: stringField(record.effective_frontmatter, "due_at") || undefined,
140
+ tags: stringList(getField(record.effective_frontmatter, role(implementation, "tags", "tags"))),
141
+ links: links(getField(record.effective_frontmatter, role(implementation, "links", "links"))),
142
+ attachments: attachments(getField(record.effective_frontmatter, role(implementation, "attachment_paths", "attachment_paths"))),
143
+ metadata: asObject(getField(record.effective_frontmatter, role(implementation, "metadata", "metadata"))) ?? {},
144
+ response,
145
+ frontmatter: record.effective_frontmatter
146
+ };
147
+ }
148
+ function normalizeResponse(record) {
149
+ const frontmatter = record.effective_frontmatter;
150
+ return {
151
+ path: record.path,
152
+ type: stringField(frontmatter, "type") || record.types[0] || "pickle_response",
153
+ responder: stringField(frontmatter, "responder") || "human",
154
+ respondedAt: stringField(frontmatter, "responded_at") || undefined,
155
+ payload: Object.fromEntries(Object.entries(frontmatter).filter(([key]) => !SYSTEM_RESPONSE_FIELDS.has(key))),
156
+ frontmatter
157
+ };
158
+ }
159
+ function requireEffectiveFrontmatter(record) {
160
+ if (!record.effective_frontmatter) {
161
+ throw new PickleContractError(`Query result ${record.path} omitted effective_frontmatter.`);
162
+ }
163
+ return record;
164
+ }
165
+ function parseFields(value) {
166
+ if (!Object.values(value).every((field) => typeof field === "string" && validFieldPath(field)))
167
+ throw new PickleContractError("The Pickle request contract is malformed.");
168
+ return value;
169
+ }
170
+ function role(implementation, name, fallback) {
171
+ return implementation.fields[name] ?? fallback;
172
+ }
173
+ function implementationForRecord(contract, record) {
174
+ const matches = contract.implementations.filter((implementation) => record.types.includes(implementation.typeName));
175
+ if (matches.length !== 1)
176
+ throw new PickleContractError(`Pickle request ${record.path} matches ${matches.length} contract implementations; exactly one is required.`);
177
+ return matches[0];
178
+ }
179
+ function getField(value, path) {
180
+ let current = value;
181
+ for (const segment of path.split(".")) {
182
+ const object = asObject(current);
183
+ if (!object)
184
+ return undefined;
185
+ current = object[segment];
186
+ }
187
+ return current;
188
+ }
189
+ function stringField(value, path) {
190
+ const field = asObject(value) ? getField(value, path) : undefined;
191
+ return typeof field === "string" ? field.trim() : "";
192
+ }
193
+ function stringList(value) {
194
+ return Array.isArray(value)
195
+ ? value.filter((item) => typeof item === "string")
196
+ : [];
197
+ }
198
+ function links(value) {
199
+ if (!Array.isArray(value))
200
+ return [];
201
+ return value.flatMap((item) => {
202
+ const object = asObject(item);
203
+ if (!object)
204
+ return [];
205
+ const label = stringField(object, "label");
206
+ const url = stringField(object, "url");
207
+ const path = stringField(object, "path");
208
+ return label || url || path
209
+ ? [{ label: label || url || path, ...(url ? { url } : {}), ...(path ? { path } : {}) }]
210
+ : [];
211
+ });
212
+ }
213
+ function attachments(value) {
214
+ return stringList(value).map((path) => ({
215
+ path,
216
+ filename: path.split("/").at(-1) || path
217
+ }));
218
+ }
219
+ function requestLink(path) {
220
+ return `[[${path.replace(/\.md$/i, "")}]]`;
221
+ }
222
+ function linkTargets(value, requestPath) {
223
+ if (typeof value !== "string")
224
+ return false;
225
+ const target = normalizeLink(value);
226
+ const request = normalizeLink(requestPath);
227
+ if (!target || !request)
228
+ return false;
229
+ return (target === request ||
230
+ (!target.includes("/") && target === request.split("/").at(-1)));
231
+ }
232
+ function normalizeLink(value) {
233
+ let normalized = value.trim();
234
+ if (normalized.startsWith("[[") && normalized.endsWith("]]")) {
235
+ normalized = normalized.slice(2, -2);
236
+ }
237
+ else {
238
+ const markdown = normalized.match(/^\[[^\]]*\]\(([^)]+)\)$/);
239
+ if (markdown)
240
+ normalized = markdown[1];
241
+ }
242
+ normalized = normalized.split("|", 1)[0].split("#", 1)[0];
243
+ return normalized
244
+ .replaceAll("\\", "/")
245
+ .replace(/^\.?\//, "")
246
+ .replace(/\.md$/i, "");
247
+ }
248
+ function validFieldPath(value) {
249
+ return value
250
+ .split(".")
251
+ .every((part) => part.length > 0 &&
252
+ part !== "__proto__" &&
253
+ part !== "prototype" &&
254
+ part !== "constructor");
255
+ }
256
+ function trimSlashes(value) {
257
+ return value.replace(/^\/+|\/+$/g, "");
258
+ }
259
+ function assertValid(value) {
260
+ if (value.valid)
261
+ return;
262
+ throw new PickleContractError(value.diagnostics[0]?.message ?? "Pickle operation failed.");
263
+ }
264
+ function asObject(value) {
265
+ return value !== null && typeof value === "object" && !Array.isArray(value)
266
+ ? value
267
+ : undefined;
268
+ }
@@ -0,0 +1,51 @@
1
+ export declare const PICKLE_REQUEST_CONTRACT_DOCUMENT = "---\nkind: mdbase.contract\ncontract_type: record\nid: pickle.request\nversion: 1.0.0\nname: Pickle request\ndescription: A portable asynchronous request that needs a human response.\nrecord_schema:\n dialect: json-schema-2020-12\n value:\n $schema: \"https://json-schema.org/draft/2020-12/schema\"\n type: object\n additionalProperties: true\n required: [title, response_type]\n properties:\n id: { type: string }\n title: { type: string, minLength: 1 }\n source: { type: string }\n message: { type: string }\n kind: { enum: [approval, choice, input, notice, message] }\n status: { enum: [pending, answered, cancelled] }\n priority: { enum: [low, normal, high, urgent] }\n response_type: { type: string }\n created_at: { type: string, format: date-time }\n tags:\n type: array\n items: { type: string }\n links:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n label: { type: string }\n url: { type: string }\n path: { type: string }\n attachment_paths:\n type: array\n items: { type: string }\n metadata:\n type: object\n additionalProperties: true\n---\n";
2
+ export declare const PICKLE_REQUEST_TYPE_DOCUMENT = "---\nkind: mdbase.type\nname: pickle_request\nversion: 1\ndescription: Async request that needs a human response.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: \"https://json-schema.org/draft/2020-12/schema\"\n type: object\n additionalProperties: true\n required: [title, response_type]\n properties:\n type: { const: pickle_request }\n id: { type: string }\n title: { type: string, minLength: 1 }\n source: { type: string }\n message: { type: string }\n kind: { enum: [approval, choice, input, notice, message] }\n status:\n enum: [pending, answered, cancelled]\n description: Legacy lifecycle marker. Response links are authoritative for answered state.\n priority: { enum: [low, normal, high, urgent] }\n response_type: { type: string }\n created_at: { type: string, format: date-time }\n due_at: { type: string, format: date-time }\n dedupe_key: { type: string }\n tags:\n type: array\n items: { type: string }\n links:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n label: { type: string }\n url: { type: string }\n path: { type: string }\n attachment_paths:\n type: array\n items: { type: string }\n metadata:\n type: object\n additionalProperties: true\n context:\n type: object\n additionalProperties: false\n properties:\n cwd: { type: string }\n repo: { type: string }\n task: { type: string }\ncollection:\n display:\n name_field: title\n unique:\n - field: id\n scope: collection\nlifecycle:\n on_create:\n set:\n created_at: { now: true }\nimplements:\n - contract: pickle.request\n version: 1.0.0\n fields:\n id: id\n title: title\n source: source\n message: message\n kind: kind\n status: status\n priority: priority\n response_type: response_type\n created_at: created_at\n tags: tags\n links: links\n attachment_paths: attachment_paths\n metadata: metadata\n---\n";
3
+ export declare const PICKLE_APPROVAL_RESPONSE_TYPE_DOCUMENT = "---\nkind: mdbase.type\nname: pickle_response_approval\nversion: 1\ndescription: Approve, reject, or request revision for a Pickle request.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: \"https://json-schema.org/draft/2020-12/schema\"\n type: object\n additionalProperties: true\n required: [request, decision]\n properties:\n type: { const: pickle_response_approval }\n id: { type: string }\n request: { type: string }\n decision: { enum: [approve, reject, revise] }\n comment: { type: string }\n responded_at: { type: string, format: date-time }\n responder: { type: string }\n attachment_paths:\n type: array\n items: { type: string }\ncollection:\n display:\n name_field: decision\n links:\n request:\n target_type: pickle_request\n validate_exists: true\n unique:\n - field: id\n scope: collection\nlifecycle:\n on_create:\n set:\n id: { ulid: true }\n responded_at: { now: true }\n---\n";
4
+ export declare const PICKLE_ACK_RESPONSE_TYPE_DOCUMENT = "---\nkind: mdbase.type\nname: pickle_response_ack\nversion: 1\ndescription: Acknowledge that a Pickle message was read.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: \"https://json-schema.org/draft/2020-12/schema\"\n type: object\n additionalProperties: true\n required: [request]\n properties:\n type: { const: pickle_response_ack }\n id: { type: string }\n request: { type: string }\n message: { type: string }\n responded_at: { type: string, format: date-time }\n responder: { type: string }\n attachment_paths:\n type: array\n items: { type: string }\ncollection:\n display:\n name_field: message\n links:\n request:\n target_type: pickle_request\n validate_exists: true\n unique:\n - field: id\n scope: collection\nlifecycle:\n on_create:\n set:\n id: { ulid: true }\n responded_at: { now: true }\n---\n";
5
+ export declare const PICKLE_TYPE_PACK_PROVISION: {
6
+ readonly manifest: {
7
+ readonly kind: "mdbase.type-pack";
8
+ readonly id: "pickle.requests";
9
+ readonly version: "1.0.0";
10
+ readonly name: "Pickle requests";
11
+ readonly description: "Pickle request and response record types.";
12
+ readonly resources: readonly [{
13
+ readonly kind: "contract";
14
+ readonly source: "contracts/pickle.request.md";
15
+ readonly target: "_contracts/pickle.request.md";
16
+ readonly digest: "sha256:caf9ee290e98ef51f871a1237b38531168aa1b74640241ce6edece4c2bff66f1";
17
+ }, {
18
+ readonly kind: "type";
19
+ readonly source: "types/pickle_request.md";
20
+ readonly target: "_types/pickle_request.md";
21
+ readonly digest: "sha256:7f84e359a5a3b7d23dd3471c7557f67cb8a80eb553184db83c4750b2af5761e4";
22
+ }, {
23
+ readonly kind: "type";
24
+ readonly source: "types/pickle_response_approval.md";
25
+ readonly target: "_types/pickle_response_approval.md";
26
+ readonly digest: "sha256:944d2b68c158297f5d2a035fa6ff3c81a7098531f77a9bc3342dc9435ffcb8b8";
27
+ }, {
28
+ readonly kind: "type";
29
+ readonly source: "types/pickle_response_ack.md";
30
+ readonly target: "_types/pickle_response_ack.md";
31
+ readonly digest: "sha256:2530c8d21bb711889f20e0d096045cb6c9a7618583b1e2afff7ada72367de41f";
32
+ }];
33
+ };
34
+ readonly resources: readonly [{
35
+ readonly source: "contracts/pickle.request.md";
36
+ readonly document: "---\nkind: mdbase.contract\ncontract_type: record\nid: pickle.request\nversion: 1.0.0\nname: Pickle request\ndescription: A portable asynchronous request that needs a human response.\nrecord_schema:\n dialect: json-schema-2020-12\n value:\n $schema: \"https://json-schema.org/draft/2020-12/schema\"\n type: object\n additionalProperties: true\n required: [title, response_type]\n properties:\n id: { type: string }\n title: { type: string, minLength: 1 }\n source: { type: string }\n message: { type: string }\n kind: { enum: [approval, choice, input, notice, message] }\n status: { enum: [pending, answered, cancelled] }\n priority: { enum: [low, normal, high, urgent] }\n response_type: { type: string }\n created_at: { type: string, format: date-time }\n tags:\n type: array\n items: { type: string }\n links:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n label: { type: string }\n url: { type: string }\n path: { type: string }\n attachment_paths:\n type: array\n items: { type: string }\n metadata:\n type: object\n additionalProperties: true\n---\n";
37
+ }, {
38
+ readonly source: "types/pickle_request.md";
39
+ readonly document: "---\nkind: mdbase.type\nname: pickle_request\nversion: 1\ndescription: Async request that needs a human response.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: \"https://json-schema.org/draft/2020-12/schema\"\n type: object\n additionalProperties: true\n required: [title, response_type]\n properties:\n type: { const: pickle_request }\n id: { type: string }\n title: { type: string, minLength: 1 }\n source: { type: string }\n message: { type: string }\n kind: { enum: [approval, choice, input, notice, message] }\n status:\n enum: [pending, answered, cancelled]\n description: Legacy lifecycle marker. Response links are authoritative for answered state.\n priority: { enum: [low, normal, high, urgent] }\n response_type: { type: string }\n created_at: { type: string, format: date-time }\n due_at: { type: string, format: date-time }\n dedupe_key: { type: string }\n tags:\n type: array\n items: { type: string }\n links:\n type: array\n items:\n type: object\n additionalProperties: false\n properties:\n label: { type: string }\n url: { type: string }\n path: { type: string }\n attachment_paths:\n type: array\n items: { type: string }\n metadata:\n type: object\n additionalProperties: true\n context:\n type: object\n additionalProperties: false\n properties:\n cwd: { type: string }\n repo: { type: string }\n task: { type: string }\ncollection:\n display:\n name_field: title\n unique:\n - field: id\n scope: collection\nlifecycle:\n on_create:\n set:\n created_at: { now: true }\nimplements:\n - contract: pickle.request\n version: 1.0.0\n fields:\n id: id\n title: title\n source: source\n message: message\n kind: kind\n status: status\n priority: priority\n response_type: response_type\n created_at: created_at\n tags: tags\n links: links\n attachment_paths: attachment_paths\n metadata: metadata\n---\n";
40
+ }, {
41
+ readonly source: "types/pickle_response_approval.md";
42
+ readonly document: "---\nkind: mdbase.type\nname: pickle_response_approval\nversion: 1\ndescription: Approve, reject, or request revision for a Pickle request.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: \"https://json-schema.org/draft/2020-12/schema\"\n type: object\n additionalProperties: true\n required: [request, decision]\n properties:\n type: { const: pickle_response_approval }\n id: { type: string }\n request: { type: string }\n decision: { enum: [approve, reject, revise] }\n comment: { type: string }\n responded_at: { type: string, format: date-time }\n responder: { type: string }\n attachment_paths:\n type: array\n items: { type: string }\ncollection:\n display:\n name_field: decision\n links:\n request:\n target_type: pickle_request\n validate_exists: true\n unique:\n - field: id\n scope: collection\nlifecycle:\n on_create:\n set:\n id: { ulid: true }\n responded_at: { now: true }\n---\n";
43
+ }, {
44
+ readonly source: "types/pickle_response_ack.md";
45
+ readonly document: "---\nkind: mdbase.type\nname: pickle_response_ack\nversion: 1\ndescription: Acknowledge that a Pickle message was read.\nschema:\n dialect: json-schema-2020-12\n value:\n $schema: \"https://json-schema.org/draft/2020-12/schema\"\n type: object\n additionalProperties: true\n required: [request]\n properties:\n type: { const: pickle_response_ack }\n id: { type: string }\n request: { type: string }\n message: { type: string }\n responded_at: { type: string, format: date-time }\n responder: { type: string }\n attachment_paths:\n type: array\n items: { type: string }\ncollection:\n display:\n name_field: message\n links:\n request:\n target_type: pickle_request\n validate_exists: true\n unique:\n - field: id\n scope: collection\nlifecycle:\n on_create:\n set:\n id: { ulid: true }\n responded_at: { now: true }\n---\n";
46
+ }];
47
+ readonly provides: readonly [{
48
+ readonly id: "pickle.request";
49
+ readonly version: "1.0.0";
50
+ }];
51
+ };
@@ -0,0 +1,258 @@
1
+ export const PICKLE_REQUEST_CONTRACT_DOCUMENT = `---
2
+ kind: mdbase.contract
3
+ contract_type: record
4
+ id: pickle.request
5
+ version: 1.0.0
6
+ name: Pickle request
7
+ description: A portable asynchronous request that needs a human response.
8
+ record_schema:
9
+ dialect: json-schema-2020-12
10
+ value:
11
+ $schema: "https://json-schema.org/draft/2020-12/schema"
12
+ type: object
13
+ additionalProperties: true
14
+ required: [title, response_type]
15
+ properties:
16
+ id: { type: string }
17
+ title: { type: string, minLength: 1 }
18
+ source: { type: string }
19
+ message: { type: string }
20
+ kind: { enum: [approval, choice, input, notice, message] }
21
+ status: { enum: [pending, answered, cancelled] }
22
+ priority: { enum: [low, normal, high, urgent] }
23
+ response_type: { type: string }
24
+ created_at: { type: string, format: date-time }
25
+ tags:
26
+ type: array
27
+ items: { type: string }
28
+ links:
29
+ type: array
30
+ items:
31
+ type: object
32
+ additionalProperties: false
33
+ properties:
34
+ label: { type: string }
35
+ url: { type: string }
36
+ path: { type: string }
37
+ attachment_paths:
38
+ type: array
39
+ items: { type: string }
40
+ metadata:
41
+ type: object
42
+ additionalProperties: true
43
+ ---
44
+ `;
45
+ export const PICKLE_REQUEST_TYPE_DOCUMENT = `---
46
+ kind: mdbase.type
47
+ name: pickle_request
48
+ version: 1
49
+ description: Async request that needs a human response.
50
+ schema:
51
+ dialect: json-schema-2020-12
52
+ value:
53
+ $schema: "https://json-schema.org/draft/2020-12/schema"
54
+ type: object
55
+ additionalProperties: true
56
+ required: [title, response_type]
57
+ properties:
58
+ type: { const: pickle_request }
59
+ id: { type: string }
60
+ title: { type: string, minLength: 1 }
61
+ source: { type: string }
62
+ message: { type: string }
63
+ kind: { enum: [approval, choice, input, notice, message] }
64
+ status:
65
+ enum: [pending, answered, cancelled]
66
+ description: Legacy lifecycle marker. Response links are authoritative for answered state.
67
+ priority: { enum: [low, normal, high, urgent] }
68
+ response_type: { type: string }
69
+ created_at: { type: string, format: date-time }
70
+ due_at: { type: string, format: date-time }
71
+ dedupe_key: { type: string }
72
+ tags:
73
+ type: array
74
+ items: { type: string }
75
+ links:
76
+ type: array
77
+ items:
78
+ type: object
79
+ additionalProperties: false
80
+ properties:
81
+ label: { type: string }
82
+ url: { type: string }
83
+ path: { type: string }
84
+ attachment_paths:
85
+ type: array
86
+ items: { type: string }
87
+ metadata:
88
+ type: object
89
+ additionalProperties: true
90
+ context:
91
+ type: object
92
+ additionalProperties: false
93
+ properties:
94
+ cwd: { type: string }
95
+ repo: { type: string }
96
+ task: { type: string }
97
+ collection:
98
+ display:
99
+ name_field: title
100
+ unique:
101
+ - field: id
102
+ scope: collection
103
+ lifecycle:
104
+ on_create:
105
+ set:
106
+ created_at: { now: true }
107
+ implements:
108
+ - contract: pickle.request
109
+ version: 1.0.0
110
+ fields:
111
+ id: id
112
+ title: title
113
+ source: source
114
+ message: message
115
+ kind: kind
116
+ status: status
117
+ priority: priority
118
+ response_type: response_type
119
+ created_at: created_at
120
+ tags: tags
121
+ links: links
122
+ attachment_paths: attachment_paths
123
+ metadata: metadata
124
+ ---
125
+ `;
126
+ export const PICKLE_APPROVAL_RESPONSE_TYPE_DOCUMENT = `---
127
+ kind: mdbase.type
128
+ name: pickle_response_approval
129
+ version: 1
130
+ description: Approve, reject, or request revision for a Pickle request.
131
+ schema:
132
+ dialect: json-schema-2020-12
133
+ value:
134
+ $schema: "https://json-schema.org/draft/2020-12/schema"
135
+ type: object
136
+ additionalProperties: true
137
+ required: [request, decision]
138
+ properties:
139
+ type: { const: pickle_response_approval }
140
+ id: { type: string }
141
+ request: { type: string }
142
+ decision: { enum: [approve, reject, revise] }
143
+ comment: { type: string }
144
+ responded_at: { type: string, format: date-time }
145
+ responder: { type: string }
146
+ attachment_paths:
147
+ type: array
148
+ items: { type: string }
149
+ collection:
150
+ display:
151
+ name_field: decision
152
+ links:
153
+ request:
154
+ target_type: pickle_request
155
+ validate_exists: true
156
+ unique:
157
+ - field: id
158
+ scope: collection
159
+ lifecycle:
160
+ on_create:
161
+ set:
162
+ id: { ulid: true }
163
+ responded_at: { now: true }
164
+ ---
165
+ `;
166
+ export const PICKLE_ACK_RESPONSE_TYPE_DOCUMENT = `---
167
+ kind: mdbase.type
168
+ name: pickle_response_ack
169
+ version: 1
170
+ description: Acknowledge that a Pickle message was read.
171
+ schema:
172
+ dialect: json-schema-2020-12
173
+ value:
174
+ $schema: "https://json-schema.org/draft/2020-12/schema"
175
+ type: object
176
+ additionalProperties: true
177
+ required: [request]
178
+ properties:
179
+ type: { const: pickle_response_ack }
180
+ id: { type: string }
181
+ request: { type: string }
182
+ message: { type: string }
183
+ responded_at: { type: string, format: date-time }
184
+ responder: { type: string }
185
+ attachment_paths:
186
+ type: array
187
+ items: { type: string }
188
+ collection:
189
+ display:
190
+ name_field: message
191
+ links:
192
+ request:
193
+ target_type: pickle_request
194
+ validate_exists: true
195
+ unique:
196
+ - field: id
197
+ scope: collection
198
+ lifecycle:
199
+ on_create:
200
+ set:
201
+ id: { ulid: true }
202
+ responded_at: { now: true }
203
+ ---
204
+ `;
205
+ export const PICKLE_TYPE_PACK_PROVISION = {
206
+ manifest: {
207
+ kind: "mdbase.type-pack",
208
+ id: "pickle.requests",
209
+ version: "1.0.0",
210
+ name: "Pickle requests",
211
+ description: "Pickle request and response record types.",
212
+ resources: [
213
+ {
214
+ kind: "contract",
215
+ source: "contracts/pickle.request.md",
216
+ target: "_contracts/pickle.request.md",
217
+ digest: "sha256:caf9ee290e98ef51f871a1237b38531168aa1b74640241ce6edece4c2bff66f1"
218
+ },
219
+ {
220
+ kind: "type",
221
+ source: "types/pickle_request.md",
222
+ target: "_types/pickle_request.md",
223
+ digest: "sha256:7f84e359a5a3b7d23dd3471c7557f67cb8a80eb553184db83c4750b2af5761e4"
224
+ },
225
+ {
226
+ kind: "type",
227
+ source: "types/pickle_response_approval.md",
228
+ target: "_types/pickle_response_approval.md",
229
+ digest: "sha256:944d2b68c158297f5d2a035fa6ff3c81a7098531f77a9bc3342dc9435ffcb8b8"
230
+ },
231
+ {
232
+ kind: "type",
233
+ source: "types/pickle_response_ack.md",
234
+ target: "_types/pickle_response_ack.md",
235
+ digest: "sha256:2530c8d21bb711889f20e0d096045cb6c9a7618583b1e2afff7ada72367de41f"
236
+ }
237
+ ]
238
+ },
239
+ resources: [
240
+ {
241
+ source: "contracts/pickle.request.md",
242
+ document: PICKLE_REQUEST_CONTRACT_DOCUMENT
243
+ },
244
+ {
245
+ source: "types/pickle_request.md",
246
+ document: PICKLE_REQUEST_TYPE_DOCUMENT
247
+ },
248
+ {
249
+ source: "types/pickle_response_approval.md",
250
+ document: PICKLE_APPROVAL_RESPONSE_TYPE_DOCUMENT
251
+ },
252
+ {
253
+ source: "types/pickle_response_ack.md",
254
+ document: PICKLE_ACK_RESPONSE_TYPE_DOCUMENT
255
+ }
256
+ ],
257
+ provides: [{ id: "pickle.request", version: "1.0.0" }]
258
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@mdbase-dev/pickle",
3
+ "version": "0.1.0-beta.21",
4
+ "private": false,
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/mdbase-dev/mdbase-connect.git",
10
+ "directory": "packages/pickle"
11
+ },
12
+ "homepage": "https://mdbase.dev",
13
+ "sideEffects": false,
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "dependencies": {
25
+ "@mdbase-dev/connect": "0.1.0-beta.21",
26
+ "@mdbase-dev/connect-protocol": "0.1.0-beta.21"
27
+ },
28
+ "devDependencies": {
29
+ "typescript": "^7.0.2",
30
+ "vitest": "^4.1.10",
31
+ "@mdbase-dev/connect-dev": "0.1.0-beta.21"
32
+ },
33
+ "scripts": {
34
+ "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
35
+ "test": "vitest run",
36
+ "typecheck": "tsc -p tsconfig.json --noEmit"
37
+ }
38
+ }