@penpair/core 0.1.2 → 0.2.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.
Files changed (123) hide show
  1. package/dist/adapters.d.ts +16 -1
  2. package/dist/adapters.js +8 -12
  3. package/dist/approvals.d.ts +32 -0
  4. package/dist/approvals.js +99 -0
  5. package/dist/atomic-output.d.ts +5 -0
  6. package/dist/atomic-output.js +28 -0
  7. package/dist/audit-rules.d.ts +60 -0
  8. package/dist/audit-rules.js +177 -0
  9. package/dist/backup-note.d.ts +4 -0
  10. package/dist/backup-note.js +28 -0
  11. package/dist/baseline-coverage.d.ts +25 -0
  12. package/dist/baseline-coverage.js +114 -0
  13. package/dist/baselines-config.d.ts +25 -0
  14. package/dist/baselines-config.js +52 -3
  15. package/dist/baselines.d.ts +37 -8
  16. package/dist/baselines.js +132 -52
  17. package/dist/cli-arguments.d.ts +7 -0
  18. package/dist/cli-arguments.js +35 -0
  19. package/dist/cli-main.d.ts +13 -1
  20. package/dist/cli-main.js +770 -139
  21. package/dist/client.d.ts +26 -5
  22. package/dist/client.js +46 -20
  23. package/dist/command-result.d.ts +159 -0
  24. package/dist/command-result.js +179 -0
  25. package/dist/coordination.d.ts +15 -0
  26. package/dist/coordination.js +122 -0
  27. package/dist/credentials.d.ts +21 -0
  28. package/dist/credentials.js +61 -0
  29. package/dist/deep-check.js +9 -5
  30. package/dist/design-spec.d.ts +55 -0
  31. package/dist/design-spec.js +135 -0
  32. package/dist/endpoint.d.ts +8 -1
  33. package/dist/endpoint.js +48 -4
  34. package/dist/errors.d.ts +2 -1
  35. package/dist/errors.js +8 -4
  36. package/dist/export.d.ts +18 -0
  37. package/dist/export.js +42 -7
  38. package/dist/file-archive.d.ts +16 -0
  39. package/dist/file-archive.js +32 -0
  40. package/dist/file-guard.d.ts +8 -0
  41. package/dist/file-guard.js +40 -0
  42. package/dist/formats.d.ts +4 -0
  43. package/dist/formats.js +28 -0
  44. package/dist/index.d.ts +33 -1
  45. package/dist/index.js +33 -1
  46. package/dist/inspection.d.ts +106 -0
  47. package/dist/inspection.js +344 -0
  48. package/dist/instance/config-schema.d.ts +422 -0
  49. package/dist/instance/config-schema.js +76 -0
  50. package/dist/instance/config.d.ts +29 -0
  51. package/dist/instance/config.js +102 -11
  52. package/dist/instance/connect.d.ts +10 -1
  53. package/dist/instance/connect.js +53 -12
  54. package/dist/instance/connection.d.ts +14 -0
  55. package/dist/instance/connection.js +13 -2
  56. package/dist/instance/init.js +1 -1
  57. package/dist/instance/setup.js +1 -1
  58. package/dist/instance/tty.js +3 -1
  59. package/dist/journal.d.ts +77 -0
  60. package/dist/journal.js +260 -0
  61. package/dist/kit-smoke.d.ts +31 -0
  62. package/dist/kit-smoke.js +83 -0
  63. package/dist/kit-source.d.ts +2 -0
  64. package/dist/kit-source.js +309 -0
  65. package/dist/kit.d.ts +25 -0
  66. package/dist/kit.js +90 -0
  67. package/dist/layout.d.ts +15 -0
  68. package/dist/layout.js +79 -0
  69. package/dist/live-smoke.d.ts +8 -0
  70. package/dist/live-smoke.js +14 -0
  71. package/dist/mcp-server.d.ts +12 -0
  72. package/dist/mcp-server.js +102 -0
  73. package/dist/native-api.d.ts +15 -0
  74. package/dist/native-api.js +42 -0
  75. package/dist/onboard.d.ts +10 -0
  76. package/dist/onboard.js +15 -0
  77. package/dist/operation.d.ts +20 -1
  78. package/dist/operation.js +113 -13
  79. package/dist/png.d.ts +5 -0
  80. package/dist/png.js +7 -0
  81. package/dist/portable-tokens.d.ts +22 -0
  82. package/dist/portable-tokens.js +134 -0
  83. package/dist/probe.d.ts +6 -2
  84. package/dist/probe.js +19 -3
  85. package/dist/project-input.d.ts +6 -0
  86. package/dist/project-input.js +51 -0
  87. package/dist/project-snippets.js +12 -7
  88. package/dist/project-status.d.ts +24 -0
  89. package/dist/project-status.js +79 -0
  90. package/dist/registry.d.ts +2 -1
  91. package/dist/registry.js +17 -3
  92. package/dist/run-file.d.ts +26 -0
  93. package/dist/run-file.js +100 -0
  94. package/dist/shots.d.ts +43 -0
  95. package/dist/shots.js +90 -0
  96. package/dist/skill-instructions.d.ts +13 -0
  97. package/dist/skill-instructions.js +75 -0
  98. package/dist/skills.d.ts +11 -0
  99. package/dist/skills.js +71 -33
  100. package/dist/snippet.d.ts +3 -2
  101. package/dist/snippet.js +12 -8
  102. package/dist/snippets/archive.d.ts +9 -0
  103. package/dist/snippets/archive.js +10 -4
  104. package/dist/snippets/read.d.ts +1 -1
  105. package/dist/snippets/read.js +71 -40
  106. package/dist/structure-snapshot.d.ts +73 -0
  107. package/dist/structure-snapshot.js +171 -0
  108. package/dist/token-edit.d.ts +53 -0
  109. package/dist/token-edit.js +197 -0
  110. package/dist/tokens-diff.d.ts +19 -6
  111. package/dist/tokens-diff.js +89 -18
  112. package/dist/version.d.ts +1 -1
  113. package/dist/version.js +1 -1
  114. package/package.json +1 -1
  115. package/skills/penpair-design/SKILL.md +12 -40
  116. package/skills/penpair-design/references/penpot-plugin-gotchas.md +56 -0
  117. package/skills/penpair-design/references/project-facts.md +1 -1
  118. package/skills/penpair-design/references/write-protocol.md +15 -3
  119. package/skills/penpair-handoff/SKILL.md +14 -0
  120. package/skills/penpair-handoff/references/handoff-flow.md +11 -0
  121. package/skills/penpair-recipes/SKILL.md +16 -0
  122. package/skills/penpair-recipes/references/build-flow.md +11 -0
  123. package/skills/penpair-setup/SKILL.md +6 -2
@@ -0,0 +1,422 @@
1
+ /** Editor hints for the core settings. Adapter-owned properties remain open; `config check` also checks semantic constraints. */
2
+ export declare const projectConfigSchema: {
3
+ readonly $schema: "https://json-schema.org/draft/2020-12/schema";
4
+ readonly title: "penpair project configuration";
5
+ readonly type: "object";
6
+ readonly required: readonly ["instance"];
7
+ readonly properties: {
8
+ readonly schemaVersion: {
9
+ readonly const: 1;
10
+ };
11
+ readonly connection: {
12
+ readonly type: "object";
13
+ readonly additionalProperties: false;
14
+ readonly required: readonly ["credential"];
15
+ readonly properties: {
16
+ readonly credential: {
17
+ readonly oneOf: readonly [{
18
+ readonly type: "object";
19
+ readonly additionalProperties: false;
20
+ readonly required: readonly ["type", "path"];
21
+ readonly properties: {
22
+ readonly type: {
23
+ readonly const: "file";
24
+ };
25
+ readonly path: {
26
+ readonly type: "string";
27
+ readonly minLength: 1;
28
+ };
29
+ };
30
+ }, {
31
+ readonly type: "object";
32
+ readonly additionalProperties: false;
33
+ readonly required: readonly ["type", "service", "account"];
34
+ readonly properties: {
35
+ readonly type: {
36
+ readonly const: "keychain";
37
+ };
38
+ readonly service: {
39
+ readonly type: "string";
40
+ readonly minLength: 1;
41
+ };
42
+ readonly account: {
43
+ readonly type: "string";
44
+ readonly minLength: 1;
45
+ };
46
+ };
47
+ }];
48
+ };
49
+ };
50
+ };
51
+ readonly audit: {
52
+ readonly type: "object";
53
+ readonly additionalProperties: false;
54
+ readonly properties: {
55
+ readonly contrast: {
56
+ readonly type: "boolean";
57
+ };
58
+ readonly library: {
59
+ readonly type: "boolean";
60
+ };
61
+ readonly touchTargets: {
62
+ readonly type: "boolean";
63
+ };
64
+ readonly names: {
65
+ readonly type: "boolean";
66
+ };
67
+ readonly blankText: {
68
+ readonly type: "boolean";
69
+ };
70
+ readonly interactiveIds: {
71
+ readonly type: "array";
72
+ readonly maxItems: 1000;
73
+ readonly items: {
74
+ readonly type: "string";
75
+ };
76
+ };
77
+ readonly allowBlank: {
78
+ readonly type: "array";
79
+ readonly maxItems: 1000;
80
+ readonly items: {
81
+ readonly type: "string";
82
+ };
83
+ };
84
+ readonly nameSegments: {
85
+ readonly type: "integer";
86
+ readonly minimum: 1;
87
+ readonly maximum: 10;
88
+ };
89
+ readonly suppressions: {
90
+ readonly type: "array";
91
+ readonly maxItems: 1000;
92
+ readonly items: {
93
+ readonly type: "object";
94
+ readonly required: readonly ["rule", "nodeId", "reason"];
95
+ readonly properties: {
96
+ readonly rule: {
97
+ readonly enum: readonly ["contrast", "library", "touch-target", "names", "blank-text"];
98
+ };
99
+ readonly nodeId: {
100
+ readonly type: "string";
101
+ };
102
+ readonly reason: {
103
+ readonly type: "string";
104
+ readonly minLength: 1;
105
+ };
106
+ };
107
+ };
108
+ };
109
+ };
110
+ };
111
+ readonly backup: {
112
+ readonly type: "object";
113
+ readonly required: readonly ["noteFile", "remindAfterDays"];
114
+ readonly properties: {
115
+ readonly noteFile: {
116
+ readonly type: "string";
117
+ readonly minLength: 1;
118
+ readonly pattern: "^(?![/\\\\])(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)).+$";
119
+ };
120
+ readonly remindAfterDays: {
121
+ readonly type: "integer";
122
+ readonly minimum: 1;
123
+ };
124
+ };
125
+ };
126
+ readonly file: {
127
+ readonly type: "object";
128
+ readonly required: readonly ["id"];
129
+ readonly properties: {
130
+ readonly id: {
131
+ readonly type: "string";
132
+ readonly minLength: 1;
133
+ readonly pattern: "\\S";
134
+ };
135
+ readonly name: {
136
+ readonly type: "string";
137
+ };
138
+ };
139
+ };
140
+ readonly instance: {
141
+ readonly type: "string";
142
+ readonly pattern: "^[a-z][a-z0-9-]{0,30}$";
143
+ };
144
+ readonly snippets: {
145
+ readonly type: "array";
146
+ readonly items: {
147
+ readonly type: "string";
148
+ readonly minLength: 1;
149
+ readonly pattern: "^(?![/\\\\])(?![A-Za-z]:[/\\\\])(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)).+$";
150
+ };
151
+ };
152
+ readonly adapters: {
153
+ readonly type: "array";
154
+ readonly items: {
155
+ readonly type: "string";
156
+ readonly minLength: 1;
157
+ };
158
+ };
159
+ readonly baselines: {
160
+ readonly type: "object";
161
+ readonly required: readonly ["page", "root", "frames"];
162
+ readonly properties: {
163
+ readonly page: {
164
+ readonly type: "string";
165
+ readonly minLength: 1;
166
+ };
167
+ readonly root: {
168
+ readonly type: "string";
169
+ readonly minLength: 1;
170
+ readonly pattern: "^(?![/\\\\])(?![A-Za-z]:[/\\\\])(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)).+$";
171
+ };
172
+ readonly coverage: {
173
+ readonly type: "object";
174
+ readonly required: readonly ["required"];
175
+ readonly properties: {
176
+ readonly required: {
177
+ readonly type: "array";
178
+ readonly minItems: 1;
179
+ readonly maxItems: 1000;
180
+ readonly items: {
181
+ readonly type: "object";
182
+ readonly required: readonly ["route", "viewport", "theme", "state", "fixture"];
183
+ readonly additionalProperties: false;
184
+ readonly properties: {
185
+ readonly viewport: {
186
+ readonly type: "object";
187
+ readonly required: readonly ["width", "height"];
188
+ readonly additionalProperties: false;
189
+ readonly properties: {
190
+ readonly width: {
191
+ readonly type: "integer";
192
+ readonly minimum: 1;
193
+ readonly maximum: 16384;
194
+ };
195
+ readonly height: {
196
+ readonly type: "integer";
197
+ readonly minimum: 1;
198
+ readonly maximum: 16384;
199
+ };
200
+ };
201
+ };
202
+ readonly theme: {
203
+ readonly type: "string";
204
+ readonly minLength: 1;
205
+ readonly maxLength: 200;
206
+ };
207
+ readonly state: {
208
+ readonly type: "string";
209
+ readonly minLength: 1;
210
+ readonly maxLength: 200;
211
+ };
212
+ readonly fixture: {
213
+ readonly type: "string";
214
+ readonly minLength: 1;
215
+ readonly maxLength: 200;
216
+ };
217
+ readonly route: {
218
+ readonly type: "string";
219
+ readonly minLength: 1;
220
+ };
221
+ };
222
+ };
223
+ };
224
+ readonly unsupported: {
225
+ readonly type: "array";
226
+ readonly maxItems: 1000;
227
+ readonly items: {
228
+ readonly required: readonly ["route", "viewport", "theme", "state", "fixture", "reason"];
229
+ readonly properties: {
230
+ readonly reason: {
231
+ readonly type: "string";
232
+ readonly minLength: 1;
233
+ };
234
+ readonly viewport: {
235
+ readonly type: "object";
236
+ readonly required: readonly ["width", "height"];
237
+ readonly additionalProperties: false;
238
+ readonly properties: {
239
+ readonly width: {
240
+ readonly type: "integer";
241
+ readonly minimum: 1;
242
+ readonly maximum: 16384;
243
+ };
244
+ readonly height: {
245
+ readonly type: "integer";
246
+ readonly minimum: 1;
247
+ readonly maximum: 16384;
248
+ };
249
+ };
250
+ };
251
+ readonly theme: {
252
+ readonly type: "string";
253
+ readonly minLength: 1;
254
+ readonly maxLength: 200;
255
+ };
256
+ readonly state: {
257
+ readonly type: "string";
258
+ readonly minLength: 1;
259
+ readonly maxLength: 200;
260
+ };
261
+ readonly fixture: {
262
+ readonly type: "string";
263
+ readonly minLength: 1;
264
+ readonly maxLength: 200;
265
+ };
266
+ readonly route: {
267
+ readonly type: "string";
268
+ readonly minLength: 1;
269
+ };
270
+ };
271
+ readonly type: "object";
272
+ readonly additionalProperties: false;
273
+ };
274
+ };
275
+ };
276
+ };
277
+ readonly threshold: {
278
+ readonly type: "number";
279
+ readonly minimum: 0;
280
+ readonly maximum: 1;
281
+ };
282
+ readonly frames: {
283
+ readonly type: "array";
284
+ readonly minItems: 1;
285
+ readonly maxItems: 1000;
286
+ readonly items: {
287
+ readonly type: "object";
288
+ readonly required: readonly ["frame", "id"];
289
+ readonly dependentRequired: {
290
+ readonly case: readonly ["route"];
291
+ };
292
+ readonly properties: {
293
+ readonly frame: {
294
+ readonly type: "string";
295
+ readonly minLength: 1;
296
+ };
297
+ readonly id: {
298
+ readonly type: "string";
299
+ readonly pattern: "^[a-z0-9][a-z0-9_-]{0,63}$";
300
+ };
301
+ readonly route: {
302
+ readonly type: "string";
303
+ readonly minLength: 1;
304
+ };
305
+ readonly case: {
306
+ readonly type: "object";
307
+ readonly required: readonly ["viewport", "theme", "state", "fixture"];
308
+ readonly additionalProperties: false;
309
+ readonly properties: {
310
+ readonly fonts: {
311
+ readonly type: "array";
312
+ readonly maxItems: 100;
313
+ readonly uniqueItems: true;
314
+ readonly items: {
315
+ readonly type: "string";
316
+ readonly minLength: 1;
317
+ };
318
+ };
319
+ readonly activeThemeIds: {
320
+ readonly type: "array";
321
+ readonly maxItems: 100;
322
+ readonly uniqueItems: true;
323
+ readonly items: {
324
+ readonly type: "string";
325
+ readonly minLength: 1;
326
+ };
327
+ };
328
+ readonly viewport: {
329
+ readonly type: "object";
330
+ readonly required: readonly ["width", "height"];
331
+ readonly additionalProperties: false;
332
+ readonly properties: {
333
+ readonly width: {
334
+ readonly type: "integer";
335
+ readonly minimum: 1;
336
+ readonly maximum: 16384;
337
+ };
338
+ readonly height: {
339
+ readonly type: "integer";
340
+ readonly minimum: 1;
341
+ readonly maximum: 16384;
342
+ };
343
+ };
344
+ };
345
+ readonly theme: {
346
+ readonly type: "string";
347
+ readonly minLength: 1;
348
+ readonly maxLength: 200;
349
+ };
350
+ readonly state: {
351
+ readonly type: "string";
352
+ readonly minLength: 1;
353
+ readonly maxLength: 200;
354
+ };
355
+ readonly fixture: {
356
+ readonly type: "string";
357
+ readonly minLength: 1;
358
+ readonly maxLength: 200;
359
+ };
360
+ };
361
+ };
362
+ };
363
+ };
364
+ };
365
+ };
366
+ };
367
+ };
368
+ readonly oneOf: readonly [{
369
+ readonly required: readonly ["profile"];
370
+ readonly properties: {
371
+ readonly profile: {
372
+ readonly type: "object";
373
+ readonly required: readonly ["type", "origin"];
374
+ readonly properties: {
375
+ readonly type: {
376
+ readonly const: "external";
377
+ };
378
+ readonly origin: {
379
+ readonly type: "string";
380
+ readonly pattern: "^https?://[^/?#]+$";
381
+ };
382
+ };
383
+ };
384
+ };
385
+ }, {
386
+ readonly required: readonly ["penpotVersion", "ports", "directory"];
387
+ readonly not: {
388
+ readonly required: readonly ["profile"];
389
+ };
390
+ readonly properties: {
391
+ readonly penpotVersion: {
392
+ readonly type: "string";
393
+ readonly pattern: "^\\d+\\.\\d+(\\.\\d+)?$";
394
+ };
395
+ readonly ports: {
396
+ readonly type: "object";
397
+ readonly required: readonly ["frontend", "mail"];
398
+ readonly properties: {
399
+ readonly frontend: {
400
+ readonly type: "integer";
401
+ readonly minimum: 1024;
402
+ readonly maximum: 65535;
403
+ };
404
+ readonly mail: {
405
+ readonly type: "integer";
406
+ readonly minimum: 1024;
407
+ readonly maximum: 65535;
408
+ };
409
+ };
410
+ };
411
+ readonly directory: {
412
+ readonly type: "string";
413
+ readonly minLength: 1;
414
+ readonly pattern: "^(?!/).+$";
415
+ };
416
+ readonly composeProject: {
417
+ readonly type: "string";
418
+ readonly pattern: "^[a-z0-9][a-z0-9_-]*$";
419
+ };
420
+ };
421
+ }];
422
+ };
@@ -0,0 +1,76 @@
1
+ const viewportSchema = { type: "object", required: ["width", "height"], additionalProperties: false, properties: { width: { type: "integer", minimum: 1, maximum: 16384 }, height: { type: "integer", minimum: 1, maximum: 16384 } } };
2
+ const caseFields = { viewport: viewportSchema, theme: { type: "string", minLength: 1, maxLength: 200 }, state: { type: "string", minLength: 1, maxLength: 200 }, fixture: { type: "string", minLength: 1, maxLength: 200 } };
3
+ const baselineCaseSchema = { type: "object", required: ["viewport", "theme", "state", "fixture"], additionalProperties: false, properties: { ...caseFields, fonts: { type: "array", maxItems: 100, uniqueItems: true, items: { type: "string", minLength: 1 } }, activeThemeIds: { type: "array", maxItems: 100, uniqueItems: true, items: { type: "string", minLength: 1 } } } };
4
+ const requiredCaseSchema = { type: "object", required: ["route", "viewport", "theme", "state", "fixture"], additionalProperties: false, properties: { route: { type: "string", minLength: 1 }, ...caseFields } };
5
+ /** Editor hints for the core settings. Adapter-owned properties remain open; `config check` also checks semantic constraints. */
6
+ export const projectConfigSchema = {
7
+ $schema: "https://json-schema.org/draft/2020-12/schema",
8
+ title: "penpair project configuration",
9
+ type: "object",
10
+ required: ["instance"],
11
+ properties: {
12
+ schemaVersion: { const: 1 },
13
+ connection: { type: "object", additionalProperties: false, required: ["credential"], properties: { credential: { oneOf: [
14
+ { type: "object", additionalProperties: false, required: ["type", "path"], properties: { type: { const: "file" }, path: { type: "string", minLength: 1 } } },
15
+ { type: "object", additionalProperties: false, required: ["type", "service", "account"], properties: { type: { const: "keychain" }, service: { type: "string", minLength: 1 }, account: { type: "string", minLength: 1 } } },
16
+ ] } } },
17
+ audit: { type: "object", additionalProperties: false, properties: {
18
+ contrast: { type: "boolean" }, library: { type: "boolean" }, touchTargets: { type: "boolean" }, names: { type: "boolean" }, blankText: { type: "boolean" },
19
+ interactiveIds: { type: "array", maxItems: 1000, items: { type: "string" } }, allowBlank: { type: "array", maxItems: 1000, items: { type: "string" } }, nameSegments: { type: "integer", minimum: 1, maximum: 10 },
20
+ suppressions: { type: "array", maxItems: 1000, items: { type: "object", required: ["rule", "nodeId", "reason"], properties: { rule: { enum: ["contrast", "library", "touch-target", "names", "blank-text"] }, nodeId: { type: "string" }, reason: { type: "string", minLength: 1 } } } },
21
+ } },
22
+ backup: { type: "object", required: ["noteFile", "remindAfterDays"], properties: { noteFile: { type: "string", minLength: 1, pattern: "^(?![/\\\\])(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)).+$" }, remindAfterDays: { type: "integer", minimum: 1 } } },
23
+ file: { type: "object", required: ["id"], properties: { id: { type: "string", minLength: 1, pattern: "\\S" }, name: { type: "string" } } },
24
+ instance: { type: "string", pattern: "^[a-z][a-z0-9-]{0,30}$" },
25
+ snippets: { type: "array", items: { type: "string", minLength: 1, pattern: "^(?![/\\\\])(?![A-Za-z]:[/\\\\])(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)).+$" } },
26
+ adapters: { type: "array", items: { type: "string", minLength: 1 } },
27
+ baselines: {
28
+ type: "object", required: ["page", "root", "frames"],
29
+ properties: {
30
+ page: { type: "string", minLength: 1 },
31
+ root: { type: "string", minLength: 1, pattern: "^(?![/\\\\])(?![A-Za-z]:[/\\\\])(?!.*(?:^|[/\\\\])\\.\\.(?:[/\\\\]|$)).+$" },
32
+ coverage: { type: "object", required: ["required"], properties: { required: { type: "array", minItems: 1, maxItems: 1000, items: requiredCaseSchema }, unsupported: { type: "array", maxItems: 1000, items: { ...requiredCaseSchema, required: [...requiredCaseSchema.required, "reason"], properties: { ...requiredCaseSchema.properties, reason: { type: "string", minLength: 1 } } } } } },
33
+ threshold: { type: "number", minimum: 0, maximum: 1 },
34
+ frames: {
35
+ type: "array", minItems: 1, maxItems: 1000,
36
+ items: {
37
+ type: "object", required: ["frame", "id"], dependentRequired: { case: ["route"] },
38
+ properties: {
39
+ frame: { type: "string", minLength: 1 },
40
+ id: { type: "string", pattern: "^[a-z0-9][a-z0-9_-]{0,63}$" },
41
+ route: { type: "string", minLength: 1 },
42
+ case: baselineCaseSchema,
43
+ },
44
+ },
45
+ },
46
+ },
47
+ },
48
+ },
49
+ oneOf: [
50
+ {
51
+ required: ["profile"],
52
+ properties: {
53
+ profile: {
54
+ type: "object", required: ["type", "origin"],
55
+ properties: { type: { const: "external" }, origin: { type: "string", pattern: "^https?://[^/?#]+$" } },
56
+ },
57
+ },
58
+ },
59
+ {
60
+ required: ["penpotVersion", "ports", "directory"],
61
+ not: { required: ["profile"] },
62
+ properties: {
63
+ penpotVersion: { type: "string", pattern: "^\\d+\\.\\d+(\\.\\d+)?$" },
64
+ ports: {
65
+ type: "object", required: ["frontend", "mail"],
66
+ properties: {
67
+ frontend: { type: "integer", minimum: 1024, maximum: 65535 },
68
+ mail: { type: "integer", minimum: 1024, maximum: 65535 },
69
+ },
70
+ },
71
+ directory: { type: "string", minLength: 1, pattern: "^(?!/).+$" },
72
+ composeProject: { type: "string", pattern: "^[a-z0-9][a-z0-9_-]*$" },
73
+ },
74
+ },
75
+ ],
76
+ };
@@ -1,6 +1,23 @@
1
+ import { type CredentialReference } from "../credentials.js";
2
+ import { type AuditRules } from "../audit-rules.js";
1
3
  import { type BaselinesConfig } from "../baselines-config.js";
2
4
  /** What a project keeps in `penpair.config.json`: no secrets, so it can be committed. */
5
+ export interface ProjectFile {
6
+ id: string;
7
+ name?: string;
8
+ }
9
+ export interface BackupReminder {
10
+ noteFile: string;
11
+ remindAfterDays: number;
12
+ }
3
13
  export interface InstanceConfig {
14
+ schemaVersion?: 1;
15
+ audit?: AuditRules;
16
+ connection?: {
17
+ credential: CredentialReference;
18
+ };
19
+ file?: ProjectFile;
20
+ backup?: BackupReminder;
4
21
  /** Short name; it becomes part of the compose project, so containers and volumes of two instances never collide. */
5
22
  instance: string;
6
23
  /** Pinned Penpot image version. Run the smoke test before changing it. */
@@ -22,6 +39,13 @@ export interface InstanceConfig {
22
39
  }
23
40
  /** A project that uses a Penpot someone else runs (a team server or the hosted service): no Docker here, only the connection. */
24
41
  export interface ExternalConfig {
42
+ schemaVersion?: 1;
43
+ audit?: AuditRules;
44
+ connection?: {
45
+ credential: CredentialReference;
46
+ };
47
+ file?: ProjectFile;
48
+ backup?: BackupReminder;
25
49
  instance: string;
26
50
  profile: {
27
51
  type: "external";
@@ -36,6 +60,8 @@ export declare const isExternal: (config: ProjectConfig) => config is ExternalCo
36
60
  export declare const configFileName = "penpair.config.json";
37
61
  export declare const testedPenpotVersion = "2.18.3";
38
62
  export declare class ConfigError extends Error {
63
+ readonly code: "missing" | "unreadable" | "invalid-json" | "invalid-config";
64
+ constructor(message: string, code?: "missing" | "unreadable" | "invalid-json" | "invalid-config");
39
65
  }
40
66
  export declare function composeProjectOf(config: InstanceConfig): string;
41
67
  export declare function validateConfig(value: unknown): InstanceConfig;
@@ -43,6 +69,9 @@ export declare function validateConfig(value: unknown): InstanceConfig;
43
69
  export declare function validateProjectConfig(value: unknown): ProjectConfig;
44
70
  /** The config of a project that runs its own Penpot; anything else cannot start, stop or back up containers. */
45
71
  export declare function requireLocal(config: ProjectConfig): InstanceConfig;
72
+ /** Missing is optional; unreadable, malformed or invalid project files always stop the caller. */
73
+ export declare function readProjectConfig(root: string): Promise<Record<string, unknown> | undefined>;
74
+ export declare function loadOptionalConfig(root: string): Promise<ProjectConfig | undefined>;
46
75
  export declare function loadConfig(root: string): Promise<ProjectConfig>;
47
76
  export declare const instancePaths: (root: string, config: InstanceConfig) => {
48
77
  directory: string;