@pikku/inspector 0.12.112 → 0.12.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/add/add-pikku-studio.js +75 -2
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.js +1 -0
- package/dist/types.d.ts +5 -0
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
4
|
import { ErrorCode } from '../error-codes.js';
|
|
5
5
|
const SCOPES = ['studio', 'team', 'project'];
|
|
@@ -110,6 +110,7 @@ const readScreen = (element, fail) => {
|
|
|
110
110
|
let scopes;
|
|
111
111
|
let component;
|
|
112
112
|
let app;
|
|
113
|
+
let agent;
|
|
113
114
|
let ok = true;
|
|
114
115
|
for (const field of element.properties) {
|
|
115
116
|
const key = propName(field);
|
|
@@ -136,6 +137,8 @@ const readScreen = (element, fail) => {
|
|
|
136
137
|
read = replaces = booleanLiteral(value);
|
|
137
138
|
else if (key === 'needsApp')
|
|
138
139
|
read = needsApp = booleanLiteral(value);
|
|
140
|
+
else if (key === 'agent')
|
|
141
|
+
read = agent = ts.isStringLiteral(value) ? value.text : undefined;
|
|
139
142
|
else if (key === 'layout')
|
|
140
143
|
read = layout = oneOf(value, ['bare']);
|
|
141
144
|
else if (key === 'scopes') {
|
|
@@ -182,6 +185,7 @@ const readScreen = (element, fail) => {
|
|
|
182
185
|
...(needsApp !== undefined ? { needsApp } : {}),
|
|
183
186
|
...(layout ? { layout } : {}),
|
|
184
187
|
...(scopes ? { scopes } : {}),
|
|
188
|
+
...(agent !== undefined ? { agent } : {}),
|
|
185
189
|
...(component ? { component } : { app: app }),
|
|
186
190
|
};
|
|
187
191
|
};
|
|
@@ -195,7 +199,8 @@ export function addPikkuStudio(node, state, logger) {
|
|
|
195
199
|
if (!ts.isCallExpression(node))
|
|
196
200
|
return;
|
|
197
201
|
const { expression, arguments: args } = node;
|
|
198
|
-
if (!ts.isIdentifier(expression) ||
|
|
202
|
+
if (!ts.isIdentifier(expression) ||
|
|
203
|
+
(expression.text !== 'definePlugin' && expression.text !== 'pikkuStudio'))
|
|
199
204
|
return;
|
|
200
205
|
const [firstArg] = args;
|
|
201
206
|
if (!firstArg || !ts.isObjectLiteralExpression(firstArg))
|
|
@@ -259,6 +264,34 @@ const packageRoot = (file) => {
|
|
|
259
264
|
dir = parent;
|
|
260
265
|
}
|
|
261
266
|
};
|
|
267
|
+
const AGENT_NAME = /^[a-z][a-z0-9-]*$/;
|
|
268
|
+
const STUDIO_AGENT = /^studio:[a-z][a-z0-9-]*$/;
|
|
269
|
+
/** The text of a file that exists and is not blank, else undefined. */
|
|
270
|
+
const readNonEmpty = (file) => {
|
|
271
|
+
try {
|
|
272
|
+
if (!statSync(file).isFile())
|
|
273
|
+
return undefined;
|
|
274
|
+
const text = readFileSync(file, 'utf-8');
|
|
275
|
+
return text.trim() === '' ? undefined : text;
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
/** The `key: value` lines of a leading `---` block; undefined when there is none. */
|
|
282
|
+
const readFrontmatter = (text) => {
|
|
283
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(text);
|
|
284
|
+
if (!match)
|
|
285
|
+
return undefined;
|
|
286
|
+
const fields = {};
|
|
287
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
288
|
+
const pair = /^([A-Za-z][\w-]*):\s*(.*?)\s*$/.exec(line);
|
|
289
|
+
if (!pair)
|
|
290
|
+
continue;
|
|
291
|
+
fields[pair[1]] = pair[2].replace(/^(['"])(.*)\1$/, '$2');
|
|
292
|
+
}
|
|
293
|
+
return fields;
|
|
294
|
+
};
|
|
262
295
|
const readCatalog = (file) => {
|
|
263
296
|
try {
|
|
264
297
|
return JSON.parse(readFileSync(file, 'utf-8'));
|
|
@@ -299,6 +332,46 @@ export function validatePikkuStudio(logger, state) {
|
|
|
299
332
|
}
|
|
300
333
|
}
|
|
301
334
|
for (const screen of studio.screens) {
|
|
335
|
+
const agent = screen.agent;
|
|
336
|
+
if (agent !== undefined) {
|
|
337
|
+
const invalid = (reason) => logger.critical(ErrorCode.STUDIO_SCREEN_AGENT_INVALID, `Screen '${screen.path}' has the agent '${agent}', ${reason}`);
|
|
338
|
+
if (agent.startsWith('studio:')) {
|
|
339
|
+
// An agent Studio ships: pikku cannot see it, so only the format is checked.
|
|
340
|
+
if (!STUDIO_AGENT.test(agent)) {
|
|
341
|
+
invalid("which is not a valid agent: use a name for an agent the addon ships (lowercase letters, digits and hyphens, starting with a letter) or 'studio:<name>' for one Studio ships");
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else if (!AGENT_NAME.test(agent)) {
|
|
345
|
+
invalid("which is not a valid agent: use a name for an agent the addon ships (lowercase letters, digits and hyphens, starting with a letter) or 'studio:<name>' for one Studio ships");
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
const text = root
|
|
349
|
+
? readNonEmpty(join(root, 'agents', `${agent}.md`))
|
|
350
|
+
: undefined;
|
|
351
|
+
const fields = text === undefined ? undefined : readFrontmatter(text);
|
|
352
|
+
if (text === undefined) {
|
|
353
|
+
invalid(`but the addon has no non-empty agents/${agent}.md`);
|
|
354
|
+
}
|
|
355
|
+
else if (!fields) {
|
|
356
|
+
invalid(`whose agents/${agent}.md has no frontmatter; it needs a title and a description`);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
for (const field of ['title', 'description']) {
|
|
360
|
+
const key = fields[field];
|
|
361
|
+
if (!key) {
|
|
362
|
+
invalid(`whose agents/${agent}.md frontmatter has no ${field}; it must be a message key`);
|
|
363
|
+
}
|
|
364
|
+
else if (!en || !Object.hasOwn(en, key)) {
|
|
365
|
+
logger.critical(ErrorCode.STUDIO_MESSAGE_KEY_MISSING, `definePlugin uses the message key '${key}', which is missing from the addon's messages/en.json`);
|
|
366
|
+
}
|
|
367
|
+
else if (field === 'title')
|
|
368
|
+
screen.agentTitle = key;
|
|
369
|
+
else
|
|
370
|
+
screen.agentDescription = key;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
302
375
|
const badge = screen.nav?.badge;
|
|
303
376
|
if (badge !== undefined && !state.functions.meta[badge]) {
|
|
304
377
|
logger.critical(ErrorCode.STUDIO_BADGE_FUNCTION_NOT_FOUND, `Screen '${screen.path}' has the nav badge '${badge}', which names no function in this addon`);
|
package/dist/error-codes.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export declare enum ErrorCode {
|
|
|
57
57
|
ADDON_CONTRIBUTORS_NOT_STATIC = "PKU362",
|
|
58
58
|
ADDON_CONTRIBUTOR_UNKNOWN = "PKU363",
|
|
59
59
|
ADDON_LEGACY_CONTRIBUTORS = "PKU364",
|
|
60
|
+
STUDIO_SCREEN_AGENT_INVALID = "PKU365",
|
|
60
61
|
DB_COLUMN_NAME_TYPE_CONTRADICTION = "PKU480",
|
|
61
62
|
DB_JSON_COLUMN_UNTYPED = "PKU481",
|
|
62
63
|
DB_FORMAT_ON_NON_STRING = "PKU482",
|
package/dist/error-codes.js
CHANGED
|
@@ -60,6 +60,7 @@ export var ErrorCode;
|
|
|
60
60
|
ErrorCode["ADDON_CONTRIBUTORS_NOT_STATIC"] = "PKU362";
|
|
61
61
|
ErrorCode["ADDON_CONTRIBUTOR_UNKNOWN"] = "PKU363";
|
|
62
62
|
ErrorCode["ADDON_LEGACY_CONTRIBUTORS"] = "PKU364";
|
|
63
|
+
ErrorCode["STUDIO_SCREEN_AGENT_INVALID"] = "PKU365";
|
|
63
64
|
// Database schema codegen warnings
|
|
64
65
|
ErrorCode["DB_COLUMN_NAME_TYPE_CONTRADICTION"] = "PKU480";
|
|
65
66
|
ErrorCode["DB_JSON_COLUMN_UNTYPED"] = "PKU481";
|
package/dist/types.d.ts
CHANGED
|
@@ -548,6 +548,11 @@ export type StudioScreenMeta = {
|
|
|
548
548
|
needsApp?: boolean;
|
|
549
549
|
layout?: 'bare';
|
|
550
550
|
scopes?: string[];
|
|
551
|
+
agent?: string;
|
|
552
|
+
/** Message key from the agent file's frontmatter, filled by validation. */
|
|
553
|
+
agentTitle?: string;
|
|
554
|
+
/** Message key from the agent file's frontmatter, filled by validation. */
|
|
555
|
+
agentDescription?: string;
|
|
551
556
|
} & ({
|
|
552
557
|
component: string;
|
|
553
558
|
app?: undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.113",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/pikkujs/pikku.git",
|
|
@@ -70,10 +70,10 @@
|
|
|
70
70
|
"zod": "^4.6.7"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@pikku/core": "^0.12.
|
|
73
|
+
"@pikku/core": "^0.12.150"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@pikku/core": "^0.12.
|
|
76
|
+
"@pikku/core": "^0.12.150",
|
|
77
77
|
"@types/node": "^26.6.5"
|
|
78
78
|
},
|
|
79
79
|
"engines": {
|