@tenonhq/dovetail-core 0.0.110 → 0.0.112
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 +1 -1
- package/dist/appUtils.js +50 -2
- package/dist/commander.js +39 -5
- package/dist/commands.js +61 -1
- package/dist/tests/commanderStrict.test.js +152 -0
- package/dist/tests/cred.test.js +8 -0
- package/dist/tests/refreshTableFilter.test.js +204 -0
- package/dist/tests/syncManifestWhitelist.test.js +112 -0
- package/package.json +1 -1
- package/release-manifest.json +203 -229
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ Quick reference:
|
|
|
26
26
|
```bash
|
|
27
27
|
npx dove watch # multi-scope watch + dashboard
|
|
28
28
|
npx dove push # build + push current files
|
|
29
|
-
npx dove refresh # pull manifest + new files
|
|
29
|
+
npx dove refresh # pull manifest + new files (aliases: pull, r)
|
|
30
30
|
npx dove build # local build only
|
|
31
31
|
npx dove deploy # deploy built artifacts
|
|
32
32
|
npx dove dashboard # update-set dashboard web UI
|
package/dist/appUtils.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.refreshAllFiles = exports.findMissingFiles = exports.syncManifest = exports.processManifest = exports.normalizeManifestKeys = exports.toSafeFolderName = exports.stampMetadataContent = void 0;
|
|
39
|
+
exports.createAndAssignUpdateSet = exports.swapScope = exports.buildFiles = exports.summarizeRecord = exports.pushFiles = exports.getAppFileList = exports.groupAppFiles = exports.processMissingFiles = exports.refreshAllFiles = exports.findMissingFiles = exports.syncManifest = exports.narrowManifestToTables = exports.processManifest = exports.normalizeManifestKeys = exports.toSafeFolderName = exports.stampMetadataContent = void 0;
|
|
40
40
|
const path_1 = __importDefault(require("path"));
|
|
41
41
|
const progress_1 = __importDefault(require("progress"));
|
|
42
42
|
const fUtils = __importStar(require("./FileUtils"));
|
|
@@ -272,6 +272,32 @@ const processManifest = async (manifest, forceWrite = false, sourcePath) => {
|
|
|
272
272
|
}
|
|
273
273
|
};
|
|
274
274
|
exports.processManifest = processManifest;
|
|
275
|
+
/**
|
|
276
|
+
* Narrow a manifest's table map to `tables`, for the FILE refresh only.
|
|
277
|
+
*
|
|
278
|
+
* Returns a NEW manifest and NEVER mutates its input — the caller must keep
|
|
279
|
+
* writing the original, full manifest to disk. Narrowing the persisted manifest
|
|
280
|
+
* would drop every other table from dove.manifest.<scope>.json and break
|
|
281
|
+
* push/watch. That non-mutation is the whole safety property of `--table`, so
|
|
282
|
+
* it lives in one testable function rather than inline in syncManifest.
|
|
283
|
+
*
|
|
284
|
+
* An empty table map means the scope holds none of the requested tables;
|
|
285
|
+
* refreshAllFiles no-ops on an empty map, so the scope is skipped untouched.
|
|
286
|
+
*/
|
|
287
|
+
const narrowManifestToTables = (manifest, tables) => {
|
|
288
|
+
if (!tables || tables.length === 0)
|
|
289
|
+
return manifest;
|
|
290
|
+
var wantedTables = {};
|
|
291
|
+
var presentTableNames = Object.keys(manifest.tables || {});
|
|
292
|
+
for (var w = 0; w < presentTableNames.length; w++) {
|
|
293
|
+
var wName = presentTableNames[w];
|
|
294
|
+
if (tables.indexOf(wName) !== -1) {
|
|
295
|
+
wantedTables[wName] = manifest.tables[wName];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return Object.assign({}, manifest, { tables: wantedTables });
|
|
299
|
+
};
|
|
300
|
+
exports.narrowManifestToTables = narrowManifestToTables;
|
|
275
301
|
const syncManifest = async (scope, options = {}) => {
|
|
276
302
|
// Top-level entry owns the collector lifecycle. Recursive calls (all-scopes
|
|
277
303
|
// → per-scope) inherit the collector via options._benchmarkCollector.
|
|
@@ -333,10 +359,31 @@ const syncManifest = async (scope, options = {}) => {
|
|
|
333
359
|
}
|
|
334
360
|
const refreshTableCount = Object.keys(newManifest.tables).length;
|
|
335
361
|
FileLogger_1.fileLogger.debug("Refreshed manifest for " + scope + ": " + refreshTableCount + " tables");
|
|
362
|
+
// The manifest is always written in FULL. Narrowing it to the --table
|
|
363
|
+
// subset would drop every other table from dove.manifest.<scope>.json and
|
|
364
|
+
// break push/watch, so the filter applies only to the file download below.
|
|
336
365
|
await fUtils.writeScopeManifest(scope, newManifest);
|
|
366
|
+
// --table gate: refresh file content for only the requested tables. Any
|
|
367
|
+
// table outside this scope's `_tables` whitelist was already dropped above,
|
|
368
|
+
// so intersecting the manifest is enough. `newManifest` is left intact —
|
|
369
|
+
// see narrowManifestToTables.
|
|
370
|
+
var refreshManifest = (0, exports.narrowManifestToTables)(newManifest, options.tables);
|
|
371
|
+
if (options.tables && options.tables.length > 0) {
|
|
372
|
+
var wantedNames = Object.keys(refreshManifest.tables);
|
|
373
|
+
if (wantedNames.length === 0) {
|
|
374
|
+
// Expected on every non-matching scope of an all-scopes filtered
|
|
375
|
+
// refresh, so this is debug — at info it would emit one noise line per
|
|
376
|
+
// scope that simply doesn't hold the table you asked for.
|
|
377
|
+
FileLogger_1.fileLogger.debug("syncManifest: scope " + scope + " holds none of the requested tables — skipping file refresh");
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
Logger_1.logger.info("Refreshing " + wantedNames.length + " of " + refreshTableCount + " tables in " +
|
|
381
|
+
scope + " (--table): " + wantedNames.join(", "));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
337
384
|
if (collector)
|
|
338
385
|
collector.startScope(scope);
|
|
339
|
-
await (0, exports.refreshAllFiles)(
|
|
386
|
+
await (0, exports.refreshAllFiles)(refreshManifest, scopeSourcePath, {
|
|
340
387
|
force: options.force,
|
|
341
388
|
benchmarkCollector: collector,
|
|
342
389
|
});
|
|
@@ -352,6 +399,7 @@ const syncManifest = async (scope, options = {}) => {
|
|
|
352
399
|
// scopes that leaked in before the whitelist gate existed.
|
|
353
400
|
var childOptions = {
|
|
354
401
|
force: options.force,
|
|
402
|
+
tables: options.tables,
|
|
355
403
|
_benchmarkCollector: collector,
|
|
356
404
|
};
|
|
357
405
|
if (declaredScopes.length > 0) {
|
package/dist/commander.js
CHANGED
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.configureCli = configureCli;
|
|
6
7
|
exports.initCommands = initCommands;
|
|
7
8
|
const commands_1 = require("./commands");
|
|
8
9
|
const allScopesCommands_1 = require("./allScopesCommands");
|
|
@@ -18,15 +19,26 @@ const clickupCommands_1 = require("./clickupCommands");
|
|
|
18
19
|
const loginCommand_1 = require("./loginCommand");
|
|
19
20
|
const knowledgeDiffCommand_1 = require("./knowledgeDiffCommand");
|
|
20
21
|
const yargs_1 = __importDefault(require("yargs"));
|
|
21
|
-
|
|
22
|
+
/**
|
|
23
|
+
* @description Registers every `dove` command, the global options, and the
|
|
24
|
+
* parser-strictness rules on a yargs instance. Split out from initCommands so
|
|
25
|
+
* tests can drive a fresh, non-exiting parser (see tests/commanderStrict.test.ts).
|
|
26
|
+
* @param {Argv} cli - A yargs instance to configure.
|
|
27
|
+
* @returns {Argv} The same instance, with all commands and options registered.
|
|
28
|
+
*/
|
|
29
|
+
function configureCli(cli) {
|
|
22
30
|
const sharedOptions = {
|
|
23
31
|
logLevel: {
|
|
24
32
|
default: "info",
|
|
25
33
|
},
|
|
26
34
|
};
|
|
27
|
-
|
|
35
|
+
return cli
|
|
36
|
+
.scriptName("dove")
|
|
28
37
|
.option("env", {
|
|
29
|
-
|
|
38
|
+
// `--env-file` / `--envFile` are long-documented spellings and are parsed
|
|
39
|
+
// from raw argv in envArg.ts. Declaring them as aliases keeps `--help` and
|
|
40
|
+
// the parser honest about what is actually accepted.
|
|
41
|
+
alias: ["e", "env-file", "envFile"],
|
|
30
42
|
type: "string",
|
|
31
43
|
describe: "Path to a .env file to load for this command (default: .env in the project root). Lets one checkout target multiple instances.",
|
|
32
44
|
})
|
|
@@ -65,7 +77,10 @@ async function initCommands() {
|
|
|
65
77
|
}, async (args) => {
|
|
66
78
|
await (0, allScopesCommands_1.watchAllScopesCommand)(args);
|
|
67
79
|
})
|
|
68
|
-
.command(
|
|
80
|
+
.command(
|
|
81
|
+
// `pull` is the verb people reach for ("pull before push") and it appears
|
|
82
|
+
// in downstream docs and scripts — alias it rather than silently no-op.
|
|
83
|
+
["refresh", "r", "pull"], "Pull latest manifest and file contents from the ServiceNow instance", (cmdArgs) => {
|
|
69
84
|
cmdArgs.options({
|
|
70
85
|
...sharedOptions,
|
|
71
86
|
force: {
|
|
@@ -79,6 +94,13 @@ async function initCommands() {
|
|
|
79
94
|
type: "string",
|
|
80
95
|
describe: "Refresh a single scope (default: all declared scopes)",
|
|
81
96
|
},
|
|
97
|
+
table: {
|
|
98
|
+
alias: "t",
|
|
99
|
+
type: "array",
|
|
100
|
+
describe: "Refresh file content for only these tables (repeatable or comma-separated). " +
|
|
101
|
+
"Must already be synced via includes._tables or includes._scopes.<scope>._tables. " +
|
|
102
|
+
"Default: every synced table.",
|
|
103
|
+
},
|
|
82
104
|
benchmark: {
|
|
83
105
|
alias: "b",
|
|
84
106
|
type: "boolean",
|
|
@@ -561,5 +583,17 @@ async function initCommands() {
|
|
|
561
583
|
}, async function (args) {
|
|
562
584
|
await (0, knowledgeDiffCommand_1.knowledgeDiffCommand)(args);
|
|
563
585
|
})
|
|
564
|
-
|
|
586
|
+
// An unrecognized command used to fall through the parser and exit 0, doing
|
|
587
|
+
// nothing — so `dove pull` (and any typo) looked like it had succeeded, which
|
|
588
|
+
// is the exact false confidence "pull before push" exists to prevent.
|
|
589
|
+
// strictCommands (not strict) is deliberate: it rejects unknown *commands*
|
|
590
|
+
// while still tolerating undeclared flags, so documented option spellings
|
|
591
|
+
// keep working. demandCommand turns a bare `dove` into help + exit 1.
|
|
592
|
+
.demandCommand(1, "Please specify a command (run `dove --help` to see them all)")
|
|
593
|
+
.strictCommands()
|
|
594
|
+
.recommendCommands()
|
|
595
|
+
.help();
|
|
596
|
+
}
|
|
597
|
+
async function initCommands() {
|
|
598
|
+
configureCli(yargs_1.default).argv;
|
|
565
599
|
}
|
package/dist/commands.js
CHANGED
|
@@ -37,6 +37,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.setLogLevel = setLogLevel;
|
|
40
|
+
exports.normalizeTableFilter = normalizeTableFilter;
|
|
41
|
+
exports.unknownTablesForScopes = unknownTablesForScopes;
|
|
40
42
|
exports.refreshCommand = refreshCommand;
|
|
41
43
|
exports.pushCommand = pushCommand;
|
|
42
44
|
exports.downloadCommand = downloadCommand;
|
|
@@ -61,15 +63,73 @@ const fs = __importStar(require("fs"));
|
|
|
61
63
|
function setLogLevel(args) {
|
|
62
64
|
Logger_1.logger.setLogLevel(args.logLevel);
|
|
63
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Normalize the --table flag into a clean list. yargs `type: "array"` already
|
|
68
|
+
* yields a string[], but accept comma-separated values too (`-t a,b`) so the
|
|
69
|
+
* flag behaves the way people reflexively type it.
|
|
70
|
+
*/
|
|
71
|
+
function normalizeTableFilter(raw) {
|
|
72
|
+
if (!raw)
|
|
73
|
+
return [];
|
|
74
|
+
const parts = Array.isArray(raw) ? raw : [raw];
|
|
75
|
+
const out = [];
|
|
76
|
+
for (const part of parts) {
|
|
77
|
+
for (const piece of String(part).split(",")) {
|
|
78
|
+
const name = piece.trim();
|
|
79
|
+
if (name !== "" && out.indexOf(name) === -1)
|
|
80
|
+
out.push(name);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Requested tables that no target scope actually syncs. A typo here would
|
|
87
|
+
* otherwise refresh nothing and look like a successful no-op, so the caller
|
|
88
|
+
* turns a non-empty result into a hard failure.
|
|
89
|
+
*/
|
|
90
|
+
function unknownTablesForScopes(tables, scope) {
|
|
91
|
+
const config = ConfigManager.getConfig();
|
|
92
|
+
const declaredScopes = scope ? [scope] : Object.keys(config.scopes || {});
|
|
93
|
+
// A project with no declared `scopes` (single-scope / legacy config) still
|
|
94
|
+
// refreshes — syncManifest falls back to the manifest's scopes. resolveConfigForScope
|
|
95
|
+
// on an unrecognized name yields the GLOBAL `_tables` whitelist, which is the
|
|
96
|
+
// right allow-list there. Without this fallback the validation below would be
|
|
97
|
+
// stricter than the refresh it guards and reject every table outright.
|
|
98
|
+
const scopeNames = declaredScopes.length > 0 ? declaredScopes : [""];
|
|
99
|
+
const allowed = [];
|
|
100
|
+
for (const scopeName of scopeNames) {
|
|
101
|
+
const resolved = ConfigManager.resolveConfigForScope(scopeName);
|
|
102
|
+
for (const table of resolved.tables || []) {
|
|
103
|
+
if (allowed.indexOf(table) === -1)
|
|
104
|
+
allowed.push(table);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return tables.filter((t) => allowed.indexOf(t) === -1);
|
|
108
|
+
}
|
|
64
109
|
async function refreshCommand(args, log = true) {
|
|
65
110
|
setLogLevel(args);
|
|
66
111
|
try {
|
|
67
112
|
if (!log)
|
|
68
113
|
setLogLevel({ logLevel: "warn" });
|
|
69
|
-
|
|
114
|
+
const tables = normalizeTableFilter(args.table);
|
|
115
|
+
if (tables.length > 0) {
|
|
116
|
+
const unknown = unknownTablesForScopes(tables, args.scope);
|
|
117
|
+
if (unknown.length > 0) {
|
|
118
|
+
const where = args.scope ? "scope '" + args.scope + "'" : "any configured scope";
|
|
119
|
+
const noun = unknown.length === 1 ? "table" : "tables";
|
|
120
|
+
const verb = unknown.length === 1 ? "is" : "are";
|
|
121
|
+
const pronoun = unknown.length === 1 ? "it" : "them";
|
|
122
|
+
throw new Error("--table: " + noun + " " + unknown.join(", ") + " " + verb + " not synced by " + where + ". " +
|
|
123
|
+
"Add " + pronoun + " to includes._tables (or includes._scopes.<scope>._tables) in dove.config.js first.");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
FileLogger_1.fileLogger.debug("Syncing manifest from instance (force=" + !!args.force +
|
|
127
|
+
", benchmark=" + !!args.benchmark +
|
|
128
|
+
", tables=" + (tables.length > 0 ? tables.join(",") : "all") + ")");
|
|
70
129
|
await AppUtils.syncManifest(args.scope, {
|
|
71
130
|
force: !!args.force,
|
|
72
131
|
benchmark: !!args.benchmark,
|
|
132
|
+
tables: tables.length > 0 ? tables : undefined,
|
|
73
133
|
});
|
|
74
134
|
Logger_1.logger.success("Refresh complete!");
|
|
75
135
|
setLogLevel(args);
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
// --- Mock setup (must be before imports) ---
|
|
7
|
+
//
|
|
8
|
+
// Every command module is mocked: these tests exercise the parser (routing +
|
|
9
|
+
// strictness), not the commands themselves, and the real handlers would hit
|
|
10
|
+
// ServiceNow.
|
|
11
|
+
jest.mock("../commands", () => ({
|
|
12
|
+
refreshCommand: jest.fn(),
|
|
13
|
+
pushCommand: jest.fn(),
|
|
14
|
+
downloadCommand: jest.fn(),
|
|
15
|
+
initCommand: jest.fn(),
|
|
16
|
+
buildCommand: jest.fn(),
|
|
17
|
+
deployCommand: jest.fn(),
|
|
18
|
+
statusCommand: jest.fn(),
|
|
19
|
+
taskClearCommand: jest.fn(),
|
|
20
|
+
}));
|
|
21
|
+
jest.mock("../allScopesCommands", () => ({
|
|
22
|
+
initScopesCommand: jest.fn(),
|
|
23
|
+
watchAllScopesCommand: jest.fn(),
|
|
24
|
+
}));
|
|
25
|
+
jest.mock("../updateSetCommands", () => ({
|
|
26
|
+
createUpdateSetCommand: jest.fn(),
|
|
27
|
+
switchUpdateSetCommand: jest.fn(),
|
|
28
|
+
listUpdateSetsCommand: jest.fn(),
|
|
29
|
+
showCurrentUpdateSetCommand: jest.fn(),
|
|
30
|
+
changeScopeCommand: jest.fn(),
|
|
31
|
+
showCurrentScopeCommand: jest.fn(),
|
|
32
|
+
}));
|
|
33
|
+
jest.mock("../dashboardCommand", () => ({ dashboardCommand: jest.fn() }));
|
|
34
|
+
jest.mock("../schemaCommand", () => ({
|
|
35
|
+
schemaPullCommand: jest.fn(),
|
|
36
|
+
schemaDiffCommand: jest.fn(),
|
|
37
|
+
schemaSnapshotsCommand: jest.fn(),
|
|
38
|
+
}));
|
|
39
|
+
jest.mock("../claudeCommand", () => ({ initClaudeCommand: jest.fn() }));
|
|
40
|
+
jest.mock("../createRecordCommand", () => ({ createRecordCommand: jest.fn() }));
|
|
41
|
+
jest.mock("../deleteRecordCommand", () => ({ deleteRecordCommand: jest.fn() }));
|
|
42
|
+
jest.mock("../reconcileCommand", () => ({ reconcileCommand: jest.fn() }));
|
|
43
|
+
jest.mock("../migrateCommand", () => ({ migrateCommand: jest.fn() }));
|
|
44
|
+
jest.mock("../loginCommand", () => ({ loginCommand: jest.fn() }));
|
|
45
|
+
jest.mock("../knowledgeDiffCommand", () => ({ knowledgeDiffCommand: jest.fn() }));
|
|
46
|
+
jest.mock("../clickupCommands", () => ({
|
|
47
|
+
clickupTasksCommand: jest.fn(),
|
|
48
|
+
clickupTaskCommand: jest.fn(),
|
|
49
|
+
clickupCreateCommand: jest.fn(),
|
|
50
|
+
clickupUpdateCommand: jest.fn(),
|
|
51
|
+
clickupCommentCommand: jest.fn(),
|
|
52
|
+
clickupSetupCommand: jest.fn(),
|
|
53
|
+
clickupTeamsCommand: jest.fn(),
|
|
54
|
+
clickupSpacesCommand: jest.fn(),
|
|
55
|
+
clickupListsCommand: jest.fn(),
|
|
56
|
+
}));
|
|
57
|
+
// --- Imports (after mocks) ---
|
|
58
|
+
const yargs_1 = __importDefault(require("yargs/yargs"));
|
|
59
|
+
const commander_1 = require("../commander");
|
|
60
|
+
const commands_1 = require("../commands");
|
|
61
|
+
/**
|
|
62
|
+
* @description Runs a fake `dove` invocation through a fresh, non-exiting parser.
|
|
63
|
+
* @param {string[]} args - Argv as the user would type it, minus the binary name.
|
|
64
|
+
* @returns {Promise<ParseResult>} The parse error (if any), yargs' captured output, and the parsed argv.
|
|
65
|
+
*/
|
|
66
|
+
async function run(args) {
|
|
67
|
+
const cli = (0, commander_1.configureCli)((0, yargs_1.default)([])).exitProcess(false);
|
|
68
|
+
const result = await new Promise(function (resolve) {
|
|
69
|
+
cli.parse(args, {}, function (error, argv, output) {
|
|
70
|
+
resolve({
|
|
71
|
+
error: error === null ? undefined : error,
|
|
72
|
+
output: output || "",
|
|
73
|
+
argv: (argv || {}),
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
// Command handlers are async; let their microtasks flush before asserting.
|
|
78
|
+
await new Promise(function (resolve) {
|
|
79
|
+
setImmediate(resolve);
|
|
80
|
+
});
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
beforeEach(function () {
|
|
84
|
+
jest.clearAllMocks();
|
|
85
|
+
});
|
|
86
|
+
describe("dove pull", function () {
|
|
87
|
+
it("routes `pull` to the refresh command", async function () {
|
|
88
|
+
const { error } = await run(["pull"]);
|
|
89
|
+
expect(error).toBeUndefined();
|
|
90
|
+
expect(commands_1.refreshCommand).toHaveBeenCalledTimes(1);
|
|
91
|
+
});
|
|
92
|
+
it("still routes the canonical `refresh` and its `r` alias", async function () {
|
|
93
|
+
await run(["refresh"]);
|
|
94
|
+
expect(commands_1.refreshCommand).toHaveBeenCalledTimes(1);
|
|
95
|
+
await run(["r"]);
|
|
96
|
+
expect(commands_1.refreshCommand).toHaveBeenCalledTimes(2);
|
|
97
|
+
});
|
|
98
|
+
it("passes refresh flags through the pull alias", async function () {
|
|
99
|
+
const { error, argv } = await run(["pull", "--scope", "x_cadso_core", "--force"]);
|
|
100
|
+
expect(error).toBeUndefined();
|
|
101
|
+
expect(argv.scope).toBe("x_cadso_core");
|
|
102
|
+
expect(argv.force).toBe(true);
|
|
103
|
+
expect(commands_1.refreshCommand).toHaveBeenCalledTimes(1);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe("unknown commands", function () {
|
|
107
|
+
it("errors instead of silently succeeding", async function () {
|
|
108
|
+
const { error } = await run(["psuh"]);
|
|
109
|
+
expect(error).toBeDefined();
|
|
110
|
+
expect(String(error)).toContain("Unknown command");
|
|
111
|
+
expect(commands_1.pushCommand).not.toHaveBeenCalled();
|
|
112
|
+
});
|
|
113
|
+
it("suggests the closest real command on a typo", async function () {
|
|
114
|
+
const { error, output } = await run(["refesh"]);
|
|
115
|
+
expect(error).toBeDefined();
|
|
116
|
+
expect(output + String(error)).toContain("Did you mean refresh?");
|
|
117
|
+
expect(commands_1.refreshCommand).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
it("errors when no command is given at all", async function () {
|
|
120
|
+
const { error } = await run([]);
|
|
121
|
+
expect(error).toBeDefined();
|
|
122
|
+
expect(String(error)).toContain("Please specify a command");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe("existing invocations keep working", function () {
|
|
126
|
+
it("accepts a known command", async function () {
|
|
127
|
+
const { error } = await run(["status"]);
|
|
128
|
+
expect(error).toBeUndefined();
|
|
129
|
+
expect(commands_1.statusCommand).toHaveBeenCalledTimes(1);
|
|
130
|
+
});
|
|
131
|
+
it("accepts push with its --diff flag and optional target positional", async function () {
|
|
132
|
+
const { error, argv } = await run(["push", "--diff", "main"]);
|
|
133
|
+
expect(error).toBeUndefined();
|
|
134
|
+
expect(argv.diff).toBe("main");
|
|
135
|
+
expect(commands_1.pushCommand).toHaveBeenCalledTimes(1);
|
|
136
|
+
});
|
|
137
|
+
it("accepts the documented --env-file spelling (not just --env)", async function () {
|
|
138
|
+
// strictCommands, not strict: undeclared flags stay tolerated and the
|
|
139
|
+
// documented env aliases all resolve to `env`. See envArg.ts.
|
|
140
|
+
const { error, argv } = await run(["status", "--env-file", ".env.prod"]);
|
|
141
|
+
expect(error).toBeUndefined();
|
|
142
|
+
expect(argv.env).toBe(".env.prod");
|
|
143
|
+
});
|
|
144
|
+
it("still routes nested subcommands", async function () {
|
|
145
|
+
const { error } = await run(["schema", "pull"]);
|
|
146
|
+
expect(error).toBeUndefined();
|
|
147
|
+
});
|
|
148
|
+
it("rejects an unknown nested subcommand", async function () {
|
|
149
|
+
const { error } = await run(["clickup", "taks"]);
|
|
150
|
+
expect(error).toBeDefined();
|
|
151
|
+
});
|
|
152
|
+
});
|
package/dist/tests/cred.test.js
CHANGED
|
@@ -3,6 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
// --- Mock setup (must be before imports) ---
|
|
7
|
+
//
|
|
8
|
+
// These tests assert on credential loading only. init() also boots the CLI
|
|
9
|
+
// parser, which under Jest sees jest's own argv, finds no `dove` command, and
|
|
10
|
+
// now exits 1 (the parser rejects unknown commands instead of silently
|
|
11
|
+
// succeeding). Stub the parser so the credential assertions are what's tested.
|
|
12
|
+
jest.mock("../commander", () => ({ initCommands: jest.fn() }));
|
|
13
|
+
// --- Imports (after mocks) ---
|
|
6
14
|
const fs_1 = __importDefault(require("fs"));
|
|
7
15
|
const path_1 = __importDefault(require("path"));
|
|
8
16
|
const bootstrap_1 = require("../bootstrap");
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for `dove refresh --table` — narrow the file refresh to specific tables.
|
|
4
|
+
*
|
|
5
|
+
* Validates:
|
|
6
|
+
* - normalizeTableFilter accepts repeated flags AND comma-separated values, trimming + de-duping
|
|
7
|
+
* - unknownTablesForScopes resolves against the target scope, or the union of all scopes
|
|
8
|
+
* - refreshCommand HARD-FAILS on a table that no scope syncs (a typo must not look like a no-op)
|
|
9
|
+
* - refreshCommand forwards the table list to syncManifest, and forwards undefined when unfiltered
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
const commands_1 = require("../commands");
|
|
46
|
+
const AppUtils = __importStar(require("../appUtils"));
|
|
47
|
+
const ConfigManager = __importStar(require("../config"));
|
|
48
|
+
// --- Mock setup (mirrors taskClear.test.ts) ---
|
|
49
|
+
var logMessages = [];
|
|
50
|
+
jest.mock("../Logger", function () {
|
|
51
|
+
return {
|
|
52
|
+
logger: {
|
|
53
|
+
setLogLevel: jest.fn(),
|
|
54
|
+
success: jest.fn(function (msg) {
|
|
55
|
+
logMessages.push({ level: "success", msg: msg });
|
|
56
|
+
}),
|
|
57
|
+
info: jest.fn(function (msg) {
|
|
58
|
+
logMessages.push({ level: "info", msg: msg });
|
|
59
|
+
}),
|
|
60
|
+
error: jest.fn(function (msg) {
|
|
61
|
+
logMessages.push({ level: "error", msg: msg });
|
|
62
|
+
}),
|
|
63
|
+
warn: jest.fn(function (msg) {
|
|
64
|
+
logMessages.push({ level: "warn", msg: msg });
|
|
65
|
+
}),
|
|
66
|
+
debug: jest.fn(),
|
|
67
|
+
getInternalLogger: jest.fn(function () {
|
|
68
|
+
return { error: jest.fn() };
|
|
69
|
+
}),
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
jest.mock("../config", function () {
|
|
74
|
+
return {
|
|
75
|
+
getConfig: jest.fn(),
|
|
76
|
+
resolveConfigForScope: jest.fn(),
|
|
77
|
+
loadConfigs: jest.fn(),
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
jest.mock("../appUtils", function () {
|
|
81
|
+
return { syncManifest: jest.fn() };
|
|
82
|
+
});
|
|
83
|
+
jest.mock("../snClient", function () {
|
|
84
|
+
return { defaultClient: jest.fn(), unwrapSNResponse: jest.fn() };
|
|
85
|
+
});
|
|
86
|
+
jest.mock("../FileLogger", function () {
|
|
87
|
+
return { fileLogger: { debug: jest.fn() } };
|
|
88
|
+
});
|
|
89
|
+
jest.mock("../logMessages", function () {
|
|
90
|
+
return { logPushResults: jest.fn(), logBuildResults: jest.fn() };
|
|
91
|
+
});
|
|
92
|
+
jest.mock("../gitUtils", function () {
|
|
93
|
+
return { gitDiffToEncodedPaths: jest.fn() };
|
|
94
|
+
});
|
|
95
|
+
jest.mock("../FileUtils", function () {
|
|
96
|
+
return { encodedPathsToFilePaths: jest.fn() };
|
|
97
|
+
});
|
|
98
|
+
jest.mock("inquirer", function () {
|
|
99
|
+
return { prompt: jest.fn() };
|
|
100
|
+
});
|
|
101
|
+
var mockConfig = ConfigManager;
|
|
102
|
+
var mockAppUtils = AppUtils;
|
|
103
|
+
// Two scopes, each syncing a different extra table — enough to prove the
|
|
104
|
+
// union-vs-single-scope resolution.
|
|
105
|
+
function stubTwoScopes() {
|
|
106
|
+
mockConfig.getConfig.mockReturnValue({
|
|
107
|
+
scopes: { x_cadso_journey: {}, x_cadso_automate: {} },
|
|
108
|
+
});
|
|
109
|
+
mockConfig.resolveConfigForScope.mockImplementation(function (scopeName) {
|
|
110
|
+
var extra = scopeName === "x_cadso_journey"
|
|
111
|
+
? "x_cadso_journey_action"
|
|
112
|
+
: "x_cadso_core_setting";
|
|
113
|
+
return { tables: ["sys_script", extra] };
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
var defaultArgs = { logLevel: "info" };
|
|
117
|
+
describe("normalizeTableFilter", function () {
|
|
118
|
+
it("returns an empty list when the flag is absent", function () {
|
|
119
|
+
expect((0, commands_1.normalizeTableFilter)(undefined)).toEqual([]);
|
|
120
|
+
expect((0, commands_1.normalizeTableFilter)("")).toEqual([]);
|
|
121
|
+
});
|
|
122
|
+
it("accepts a single string", function () {
|
|
123
|
+
expect((0, commands_1.normalizeTableFilter)("sys_script")).toEqual(["sys_script"]);
|
|
124
|
+
});
|
|
125
|
+
it("accepts a repeated flag (yargs array)", function () {
|
|
126
|
+
expect((0, commands_1.normalizeTableFilter)(["sys_script", "sys_ui_action"])).toEqual([
|
|
127
|
+
"sys_script",
|
|
128
|
+
"sys_ui_action",
|
|
129
|
+
]);
|
|
130
|
+
});
|
|
131
|
+
it("splits comma-separated values", function () {
|
|
132
|
+
expect((0, commands_1.normalizeTableFilter)("sys_script,sys_ui_action")).toEqual([
|
|
133
|
+
"sys_script",
|
|
134
|
+
"sys_ui_action",
|
|
135
|
+
]);
|
|
136
|
+
});
|
|
137
|
+
it("trims whitespace and de-dupes", function () {
|
|
138
|
+
expect((0, commands_1.normalizeTableFilter)([" sys_script , sys_ui_action ", "sys_script"])).toEqual([
|
|
139
|
+
"sys_script",
|
|
140
|
+
"sys_ui_action",
|
|
141
|
+
]);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe("unknownTablesForScopes", function () {
|
|
145
|
+
beforeEach(function () {
|
|
146
|
+
logMessages = [];
|
|
147
|
+
jest.clearAllMocks();
|
|
148
|
+
stubTwoScopes();
|
|
149
|
+
});
|
|
150
|
+
it("accepts a table synced by the named scope", function () {
|
|
151
|
+
expect((0, commands_1.unknownTablesForScopes)(["x_cadso_journey_action"], "x_cadso_journey")).toEqual([]);
|
|
152
|
+
});
|
|
153
|
+
it("rejects a table that belongs to a DIFFERENT scope when one is named", function () {
|
|
154
|
+
expect((0, commands_1.unknownTablesForScopes)(["x_cadso_core_setting"], "x_cadso_journey")).toEqual(["x_cadso_core_setting"]);
|
|
155
|
+
});
|
|
156
|
+
it("accepts a table from any scope when no scope is named", function () {
|
|
157
|
+
expect((0, commands_1.unknownTablesForScopes)(["x_cadso_core_setting"])).toEqual([]);
|
|
158
|
+
});
|
|
159
|
+
it("reports tables no scope syncs", function () {
|
|
160
|
+
expect((0, commands_1.unknownTablesForScopes)(["sys_script", "nope_not_a_table"])).toEqual([
|
|
161
|
+
"nope_not_a_table",
|
|
162
|
+
]);
|
|
163
|
+
});
|
|
164
|
+
// Regression: a project with no declared `scopes` (single-scope / legacy
|
|
165
|
+
// config) still refreshes — syncManifest falls back to the manifest's scopes.
|
|
166
|
+
// Validating against an empty scope list would make the allow-list empty and
|
|
167
|
+
// reject EVERY table, i.e. be stricter than the refresh it guards.
|
|
168
|
+
it("falls back to the global _tables when the config declares no scopes", function () {
|
|
169
|
+
mockConfig.getConfig.mockReturnValue({}); // no `scopes` key
|
|
170
|
+
mockConfig.resolveConfigForScope.mockImplementation(function () {
|
|
171
|
+
return { tables: ["sys_script", "sys_script_include"] };
|
|
172
|
+
});
|
|
173
|
+
expect((0, commands_1.unknownTablesForScopes)(["sys_script"])).toEqual([]);
|
|
174
|
+
expect((0, commands_1.unknownTablesForScopes)(["nope_not_a_table"])).toEqual(["nope_not_a_table"]);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe("refreshCommand --table", function () {
|
|
178
|
+
beforeEach(function () {
|
|
179
|
+
logMessages = [];
|
|
180
|
+
jest.clearAllMocks();
|
|
181
|
+
stubTwoScopes();
|
|
182
|
+
mockAppUtils.syncManifest.mockResolvedValue(undefined);
|
|
183
|
+
});
|
|
184
|
+
it("hard-fails on an unsynced table and does NOT refresh", async function () {
|
|
185
|
+
await expect((0, commands_1.refreshCommand)({
|
|
186
|
+
...defaultArgs,
|
|
187
|
+
scope: "x_cadso_journey",
|
|
188
|
+
table: ["nope_not_a_table"],
|
|
189
|
+
})).rejects.toThrow(/nope_not_a_table is not synced by scope 'x_cadso_journey'/);
|
|
190
|
+
expect(mockAppUtils.syncManifest).not.toHaveBeenCalled();
|
|
191
|
+
});
|
|
192
|
+
it("forwards the normalized table list to syncManifest", async function () {
|
|
193
|
+
await (0, commands_1.refreshCommand)({
|
|
194
|
+
...defaultArgs,
|
|
195
|
+
scope: "x_cadso_journey",
|
|
196
|
+
table: "x_cadso_journey_action",
|
|
197
|
+
});
|
|
198
|
+
expect(mockAppUtils.syncManifest).toHaveBeenCalledWith("x_cadso_journey", expect.objectContaining({ tables: ["x_cadso_journey_action"] }));
|
|
199
|
+
});
|
|
200
|
+
it("forwards undefined when no --table is given (unfiltered default)", async function () {
|
|
201
|
+
await (0, commands_1.refreshCommand)({ ...defaultArgs, scope: "x_cadso_journey" });
|
|
202
|
+
expect(mockAppUtils.syncManifest).toHaveBeenCalledWith("x_cadso_journey", expect.objectContaining({ tables: undefined }));
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -67,6 +67,10 @@ var mockFileLogger = {
|
|
|
67
67
|
};
|
|
68
68
|
var mockClient = {
|
|
69
69
|
getManifest: jest.fn(),
|
|
70
|
+
// refreshAllFiles asks for file content per table. Returning {} means "no
|
|
71
|
+
// files to write", which keeps these tests off the plugin/write path while
|
|
72
|
+
// still recording WHICH tables were asked for.
|
|
73
|
+
getMissingFiles: jest.fn().mockResolvedValue({}),
|
|
70
74
|
};
|
|
71
75
|
var mockFUtils = {
|
|
72
76
|
writeScopeManifest: jest.fn().mockResolvedValue(undefined),
|
|
@@ -190,3 +194,111 @@ describe("syncManifest — scope + table whitelist gates", function () {
|
|
|
190
194
|
expect(refreshedScopes).not.toContain("x_cadso_ti_agile");
|
|
191
195
|
});
|
|
192
196
|
});
|
|
197
|
+
/**
|
|
198
|
+
* `dove refresh --table` narrows the FILE refresh only.
|
|
199
|
+
*
|
|
200
|
+
* The load-bearing invariant is that the PERSISTED manifest stays complete:
|
|
201
|
+
* narrowing dove.manifest.<scope>.json to the requested table would drop every
|
|
202
|
+
* other table from it and break push/watch. These tests exist so that a future
|
|
203
|
+
* "optimization" that narrows newManifest fails loudly instead of silently
|
|
204
|
+
* corrupting the manifest.
|
|
205
|
+
*/
|
|
206
|
+
describe("syncManifest — --table filter", function () {
|
|
207
|
+
function recordWithFile(name, sysId) {
|
|
208
|
+
return { records: { [name]: { name: name, sys_id: sysId, files: [{ name: "script", type: "js" }] } } };
|
|
209
|
+
}
|
|
210
|
+
beforeEach(function () {
|
|
211
|
+
jest.clearAllMocks();
|
|
212
|
+
mockClient.getMissingFiles.mockResolvedValue({});
|
|
213
|
+
mockConfig.isMultiScopeManifest.mockReturnValue(true);
|
|
214
|
+
mockConfig.resolveConfigForScope.mockImplementation(function (_scope) {
|
|
215
|
+
return {
|
|
216
|
+
tables: ["sys_script_include", "sys_script", "sys_ux_macroponent"],
|
|
217
|
+
fieldOverrides: {},
|
|
218
|
+
apiIncludes: {},
|
|
219
|
+
apiExcludes: {},
|
|
220
|
+
};
|
|
221
|
+
});
|
|
222
|
+
mockConfig.getConfig.mockReturnValue({ scopes: { x_cadso_core: {} } });
|
|
223
|
+
mockConfig.getManifest.mockResolvedValue({
|
|
224
|
+
x_cadso_core: { scope: "x_cadso_core", tables: {} },
|
|
225
|
+
});
|
|
226
|
+
mockClient.getManifest.mockResolvedValue({
|
|
227
|
+
scope: "x_cadso_core",
|
|
228
|
+
tables: {
|
|
229
|
+
sys_script_include: recordWithFile("FooInclude", "a1"),
|
|
230
|
+
sys_script: recordWithFile("BarBR", "a2"),
|
|
231
|
+
sys_ux_macroponent: recordWithFile("BazComp", "a3"),
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
test("does NOT narrow the persisted manifest — all whitelisted tables still written", async function () {
|
|
236
|
+
await AppUtils.syncManifest("x_cadso_core", { tables: ["sys_script"] });
|
|
237
|
+
expect(mockFUtils.writeScopeManifest).toHaveBeenCalledTimes(1);
|
|
238
|
+
var writtenTables = Object.keys(mockFUtils.writeScopeManifest.mock.calls[0][1].tables);
|
|
239
|
+
// The manifest keeps every whitelisted table, not just the requested one.
|
|
240
|
+
expect(writtenTables.sort()).toEqual([
|
|
241
|
+
"sys_script",
|
|
242
|
+
"sys_script_include",
|
|
243
|
+
"sys_ux_macroponent",
|
|
244
|
+
]);
|
|
245
|
+
});
|
|
246
|
+
test("narrows the file refresh to the requested tables only", async function () {
|
|
247
|
+
await AppUtils.syncManifest("x_cadso_core", { tables: ["sys_script"] });
|
|
248
|
+
expect(mockClient.getMissingFiles).toHaveBeenCalledTimes(1);
|
|
249
|
+
var askedFor = Object.keys(mockClient.getMissingFiles.mock.calls[0][0]);
|
|
250
|
+
expect(askedFor).toEqual(["sys_script"]);
|
|
251
|
+
});
|
|
252
|
+
test("refreshes every table when no --table filter is given", async function () {
|
|
253
|
+
await AppUtils.syncManifest("x_cadso_core", {});
|
|
254
|
+
var askedFor = Object.keys(mockClient.getMissingFiles.mock.calls[0][0]);
|
|
255
|
+
expect(askedFor.sort()).toEqual([
|
|
256
|
+
"sys_script",
|
|
257
|
+
"sys_script_include",
|
|
258
|
+
"sys_ux_macroponent",
|
|
259
|
+
]);
|
|
260
|
+
});
|
|
261
|
+
test("a scope holding none of the requested tables still writes its manifest, but refreshes no files", async function () {
|
|
262
|
+
await AppUtils.syncManifest("x_cadso_core", { tables: ["sys_ui_action"] });
|
|
263
|
+
// Manifest untouched-in-full …
|
|
264
|
+
expect(mockFUtils.writeScopeManifest).toHaveBeenCalledTimes(1);
|
|
265
|
+
expect(Object.keys(mockFUtils.writeScopeManifest.mock.calls[0][1].tables)).toHaveLength(3);
|
|
266
|
+
// … and not a single file downloaded.
|
|
267
|
+
expect(mockClient.getMissingFiles).not.toHaveBeenCalled();
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
describe("narrowManifestToTables", function () {
|
|
271
|
+
var manifest;
|
|
272
|
+
beforeEach(function () {
|
|
273
|
+
manifest = {
|
|
274
|
+
scope: "x_cadso_core",
|
|
275
|
+
tables: {
|
|
276
|
+
sys_script: { records: {} },
|
|
277
|
+
sys_script_include: { records: {} },
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
test("returns the SAME manifest when no tables are requested", function () {
|
|
282
|
+
expect(AppUtils.narrowManifestToTables(manifest, undefined)).toBe(manifest);
|
|
283
|
+
expect(AppUtils.narrowManifestToTables(manifest, [])).toBe(manifest);
|
|
284
|
+
});
|
|
285
|
+
test("returns a NEW manifest and never mutates the input", function () {
|
|
286
|
+
var narrowed = AppUtils.narrowManifestToTables(manifest, ["sys_script"]);
|
|
287
|
+
expect(narrowed).not.toBe(manifest);
|
|
288
|
+
expect(Object.keys(narrowed.tables)).toEqual(["sys_script"]);
|
|
289
|
+
// The corruption guard: the caller still writes `manifest` to disk in full.
|
|
290
|
+
expect(Object.keys(manifest.tables).sort()).toEqual([
|
|
291
|
+
"sys_script",
|
|
292
|
+
"sys_script_include",
|
|
293
|
+
]);
|
|
294
|
+
});
|
|
295
|
+
test("yields an empty table map when nothing matches", function () {
|
|
296
|
+
var narrowed = AppUtils.narrowManifestToTables(manifest, ["sys_ui_action"]);
|
|
297
|
+
expect(Object.keys(narrowed.tables)).toEqual([]);
|
|
298
|
+
expect(narrowed.scope).toBe("x_cadso_core");
|
|
299
|
+
});
|
|
300
|
+
test("ignores requested tables the scope does not hold", function () {
|
|
301
|
+
var narrowed = AppUtils.narrowManifestToTables(manifest, ["sys_script", "sys_ui_action"]);
|
|
302
|
+
expect(Object.keys(narrowed.tables)).toEqual(["sys_script"]);
|
|
303
|
+
});
|
|
304
|
+
});
|
package/package.json
CHANGED
package/release-manifest.json
CHANGED
|
@@ -1,7 +1,209 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "dovetail-manifest/v1",
|
|
3
|
-
"generated_at": "2026-
|
|
3
|
+
"generated_at": "2026-07-14T18:00:31.248Z",
|
|
4
4
|
"events": [
|
|
5
|
+
{
|
|
6
|
+
"$schema": "dovetail-release-event/v1",
|
|
7
|
+
"event_id": "dovetail-core@0.0.111",
|
|
8
|
+
"package": "@tenonhq/dovetail-core",
|
|
9
|
+
"version": "0.0.111",
|
|
10
|
+
"prev_version": "0.0.110",
|
|
11
|
+
"published_at": "2026-07-14T18:00:31.240Z",
|
|
12
|
+
"git": {
|
|
13
|
+
"sha": "c017d194ec18d0293a0a512f835dafcf49f16560",
|
|
14
|
+
"tag": "dovetail-core@0.0.111"
|
|
15
|
+
},
|
|
16
|
+
"semver_bump": "patch",
|
|
17
|
+
"commits": [
|
|
18
|
+
{
|
|
19
|
+
"sha": "c017d194e",
|
|
20
|
+
"subject": "feat(core): add --table filter to dove refresh (#218)",
|
|
21
|
+
"pr": 218
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"sha": "34c5ae1ba",
|
|
25
|
+
"subject": "chore(release): @tenonhq/dovetail-deploy@0.0.2 [skip ci]"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"sha": "ef9e73505",
|
|
29
|
+
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"sha": "619cf397b",
|
|
33
|
+
"subject": "chore(release): @tenonhq/dovetail-dashboard@0.0.48 [skip ci]"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"sha": "534f517e4",
|
|
37
|
+
"subject": "chore(release): publish 3 packages [skip ci]"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"exports_delta": null,
|
|
41
|
+
"narrative": null
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"$schema": "dovetail-release-event/v1",
|
|
45
|
+
"event_id": "dovetail-deploy@0.0.2",
|
|
46
|
+
"package": "@tenonhq/dovetail-deploy",
|
|
47
|
+
"version": "0.0.2",
|
|
48
|
+
"prev_version": "0.0.1",
|
|
49
|
+
"published_at": "2026-07-10T18:41:37.089Z",
|
|
50
|
+
"git": {
|
|
51
|
+
"sha": "58c416a421d7b9eaff3483468601cc1eda753b20",
|
|
52
|
+
"tag": "dovetail-deploy@0.0.2"
|
|
53
|
+
},
|
|
54
|
+
"semver_bump": "patch",
|
|
55
|
+
"commits": [
|
|
56
|
+
{
|
|
57
|
+
"sha": "58c416a42",
|
|
58
|
+
"subject": "feat(deploy): dynamic per-developer promotion source (#211)",
|
|
59
|
+
"pr": 211
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"exports_delta": null,
|
|
63
|
+
"narrative": null
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"$schema": "dovetail-release-event/v1",
|
|
67
|
+
"event_id": "dovetail-servicenow@0.0.26",
|
|
68
|
+
"package": "@tenonhq/dovetail-servicenow",
|
|
69
|
+
"version": "0.0.26",
|
|
70
|
+
"prev_version": "0.0.25",
|
|
71
|
+
"published_at": "2026-07-10T16:27:58.460Z",
|
|
72
|
+
"git": {
|
|
73
|
+
"sha": "9b3b58b8fb9aa53b32473c8c7711446f5a52a576",
|
|
74
|
+
"tag": "dovetail-servicenow@0.0.26"
|
|
75
|
+
},
|
|
76
|
+
"semver_bump": "patch",
|
|
77
|
+
"commits": [
|
|
78
|
+
{
|
|
79
|
+
"sha": "9b3b58b8f",
|
|
80
|
+
"subject": "feat: invoke arbitrary authenticated Scripted REST operations (invoke-rest) (#214)",
|
|
81
|
+
"pr": 214
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"exports_delta": null,
|
|
85
|
+
"narrative": null
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"$schema": "dovetail-release-event/v1",
|
|
89
|
+
"event_id": "dovetail-mcp@0.0.23",
|
|
90
|
+
"package": "@tenonhq/dovetail-mcp",
|
|
91
|
+
"version": "0.0.23",
|
|
92
|
+
"prev_version": "0.0.22",
|
|
93
|
+
"published_at": "2026-07-10T16:27:58.460Z",
|
|
94
|
+
"git": {
|
|
95
|
+
"sha": "9b3b58b8fb9aa53b32473c8c7711446f5a52a576",
|
|
96
|
+
"tag": "dovetail-mcp@0.0.23"
|
|
97
|
+
},
|
|
98
|
+
"semver_bump": "patch",
|
|
99
|
+
"commits": [
|
|
100
|
+
{
|
|
101
|
+
"sha": "534f517e4",
|
|
102
|
+
"subject": "chore(release): publish 3 packages [skip ci]"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"exports_delta": null,
|
|
106
|
+
"narrative": null
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"$schema": "dovetail-release-event/v1",
|
|
110
|
+
"event_id": "dovetail-dashboard@0.0.48",
|
|
111
|
+
"package": "@tenonhq/dovetail-dashboard",
|
|
112
|
+
"version": "0.0.48",
|
|
113
|
+
"prev_version": "0.0.47",
|
|
114
|
+
"published_at": "2026-07-08T21:55:56.082Z",
|
|
115
|
+
"git": {
|
|
116
|
+
"sha": "7ef92071de6726ccfd751ae617a661f95fe685fc",
|
|
117
|
+
"tag": "dovetail-dashboard@0.0.48"
|
|
118
|
+
},
|
|
119
|
+
"semver_bump": "patch",
|
|
120
|
+
"commits": [
|
|
121
|
+
{
|
|
122
|
+
"sha": "7ef92071d",
|
|
123
|
+
"subject": "feat(ci): Slack alerts for publish success/failure + cut dashboard release (#209)",
|
|
124
|
+
"pr": 209
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"exports_delta": null,
|
|
128
|
+
"narrative": null
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"$schema": "dovetail-release-event/v1",
|
|
132
|
+
"event_id": "dovetail-clickup@0.0.13",
|
|
133
|
+
"package": "@tenonhq/dovetail-clickup",
|
|
134
|
+
"version": "0.0.13",
|
|
135
|
+
"prev_version": "0.0.12",
|
|
136
|
+
"published_at": "2026-07-08T18:19:16.768Z",
|
|
137
|
+
"git": {
|
|
138
|
+
"sha": "84fc7a200e0840aaa4b2c93d6764dce959bb6536",
|
|
139
|
+
"tag": "dovetail-clickup@0.0.13"
|
|
140
|
+
},
|
|
141
|
+
"semver_bump": "patch",
|
|
142
|
+
"commits": [
|
|
143
|
+
{
|
|
144
|
+
"sha": "84fc7a200",
|
|
145
|
+
"subject": "fix: clickup get_task resolves custom ids instead of a 401 (#208)",
|
|
146
|
+
"pr": 208
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"sha": "4bc78bc61",
|
|
150
|
+
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"sha": "c82439b30",
|
|
154
|
+
"subject": "chore(deps): bump js-yaml, jest and ts-jest (#183)",
|
|
155
|
+
"pr": 183
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"sha": "9332fdf7a",
|
|
159
|
+
"subject": "chore(release): publish 20 packages [skip ci]"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"sha": "9046da774",
|
|
163
|
+
"subject": "chore(release): publish 19 packages [skip ci]"
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
"exports_delta": null,
|
|
167
|
+
"narrative": null
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"$schema": "dovetail-release-event/v1",
|
|
171
|
+
"event_id": "dovetail-core@0.0.110",
|
|
172
|
+
"package": "@tenonhq/dovetail-core",
|
|
173
|
+
"version": "0.0.110",
|
|
174
|
+
"prev_version": "0.0.109",
|
|
175
|
+
"published_at": "2026-07-08T18:19:16.768Z",
|
|
176
|
+
"git": {
|
|
177
|
+
"sha": "84fc7a200e0840aaa4b2c93d6764dce959bb6536",
|
|
178
|
+
"tag": "dovetail-core@0.0.110"
|
|
179
|
+
},
|
|
180
|
+
"semver_bump": "patch",
|
|
181
|
+
"commits": [],
|
|
182
|
+
"exports_delta": null,
|
|
183
|
+
"narrative": null
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"$schema": "dovetail-release-event/v1",
|
|
187
|
+
"event_id": "dovetail-mcp@0.0.22",
|
|
188
|
+
"package": "@tenonhq/dovetail-mcp",
|
|
189
|
+
"version": "0.0.22",
|
|
190
|
+
"prev_version": "0.0.21",
|
|
191
|
+
"published_at": "2026-07-08T18:19:16.768Z",
|
|
192
|
+
"git": {
|
|
193
|
+
"sha": "84fc7a200e0840aaa4b2c93d6764dce959bb6536",
|
|
194
|
+
"tag": "dovetail-mcp@0.0.22"
|
|
195
|
+
},
|
|
196
|
+
"semver_bump": "patch",
|
|
197
|
+
"commits": [
|
|
198
|
+
{
|
|
199
|
+
"sha": "84fc7a200",
|
|
200
|
+
"subject": "fix: clickup get_task resolves custom ids instead of a 401 (#208)",
|
|
201
|
+
"pr": 208
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
"exports_delta": null,
|
|
205
|
+
"narrative": null
|
|
206
|
+
},
|
|
5
207
|
{
|
|
6
208
|
"$schema": "dovetail-release-event/v1",
|
|
7
209
|
"event_id": "dovetail-dashboard@0.0.44",
|
|
@@ -1075,234 +1277,6 @@
|
|
|
1075
1277
|
],
|
|
1076
1278
|
"exports_delta": null,
|
|
1077
1279
|
"narrative": null
|
|
1078
|
-
},
|
|
1079
|
-
{
|
|
1080
|
-
"$schema": "dovetail-release-event/v1",
|
|
1081
|
-
"event_id": "dovetail-prettier-plugin@0.0.11",
|
|
1082
|
-
"package": "@tenonhq/dovetail-prettier-plugin",
|
|
1083
|
-
"version": "0.0.11",
|
|
1084
|
-
"prev_version": "0.0.10",
|
|
1085
|
-
"published_at": "2026-06-05T06:43:18.252Z",
|
|
1086
|
-
"git": {
|
|
1087
|
-
"sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
|
|
1088
|
-
"tag": "dovetail-prettier-plugin@0.0.11"
|
|
1089
|
-
},
|
|
1090
|
-
"semver_bump": "patch",
|
|
1091
|
-
"commits": [
|
|
1092
|
-
{
|
|
1093
|
-
"sha": "71bdf9bba",
|
|
1094
|
-
"subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
|
|
1095
|
-
"pr": 148
|
|
1096
|
-
},
|
|
1097
|
-
{
|
|
1098
|
-
"sha": "9332fdf7a",
|
|
1099
|
-
"subject": "chore(release): publish 20 packages [skip ci]"
|
|
1100
|
-
},
|
|
1101
|
-
{
|
|
1102
|
-
"sha": "9046da774",
|
|
1103
|
-
"subject": "chore(release): publish 19 packages [skip ci]"
|
|
1104
|
-
}
|
|
1105
|
-
],
|
|
1106
|
-
"exports_delta": null,
|
|
1107
|
-
"narrative": null
|
|
1108
|
-
},
|
|
1109
|
-
{
|
|
1110
|
-
"$schema": "dovetail-release-event/v1",
|
|
1111
|
-
"event_id": "dovetail-sass-plugin@0.0.11",
|
|
1112
|
-
"package": "@tenonhq/dovetail-sass-plugin",
|
|
1113
|
-
"version": "0.0.11",
|
|
1114
|
-
"prev_version": "0.0.10",
|
|
1115
|
-
"published_at": "2026-06-05T06:43:18.252Z",
|
|
1116
|
-
"git": {
|
|
1117
|
-
"sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
|
|
1118
|
-
"tag": "dovetail-sass-plugin@0.0.11"
|
|
1119
|
-
},
|
|
1120
|
-
"semver_bump": "patch",
|
|
1121
|
-
"commits": [
|
|
1122
|
-
{
|
|
1123
|
-
"sha": "71bdf9bba",
|
|
1124
|
-
"subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
|
|
1125
|
-
"pr": 148
|
|
1126
|
-
},
|
|
1127
|
-
{
|
|
1128
|
-
"sha": "9332fdf7a",
|
|
1129
|
-
"subject": "chore(release): publish 20 packages [skip ci]"
|
|
1130
|
-
},
|
|
1131
|
-
{
|
|
1132
|
-
"sha": "9046da774",
|
|
1133
|
-
"subject": "chore(release): publish 19 packages [skip ci]"
|
|
1134
|
-
}
|
|
1135
|
-
],
|
|
1136
|
-
"exports_delta": null,
|
|
1137
|
-
"narrative": null
|
|
1138
|
-
},
|
|
1139
|
-
{
|
|
1140
|
-
"$schema": "dovetail-release-event/v1",
|
|
1141
|
-
"event_id": "dovetail-typescript-plugin@0.0.11",
|
|
1142
|
-
"package": "@tenonhq/dovetail-typescript-plugin",
|
|
1143
|
-
"version": "0.0.11",
|
|
1144
|
-
"prev_version": "0.0.10",
|
|
1145
|
-
"published_at": "2026-06-05T06:43:18.252Z",
|
|
1146
|
-
"git": {
|
|
1147
|
-
"sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
|
|
1148
|
-
"tag": "dovetail-typescript-plugin@0.0.11"
|
|
1149
|
-
},
|
|
1150
|
-
"semver_bump": "patch",
|
|
1151
|
-
"commits": [
|
|
1152
|
-
{
|
|
1153
|
-
"sha": "71bdf9bba",
|
|
1154
|
-
"subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
|
|
1155
|
-
"pr": 148
|
|
1156
|
-
},
|
|
1157
|
-
{
|
|
1158
|
-
"sha": "9332fdf7a",
|
|
1159
|
-
"subject": "chore(release): publish 20 packages [skip ci]"
|
|
1160
|
-
},
|
|
1161
|
-
{
|
|
1162
|
-
"sha": "9046da774",
|
|
1163
|
-
"subject": "chore(release): publish 19 packages [skip ci]"
|
|
1164
|
-
}
|
|
1165
|
-
],
|
|
1166
|
-
"exports_delta": null,
|
|
1167
|
-
"narrative": null
|
|
1168
|
-
},
|
|
1169
|
-
{
|
|
1170
|
-
"$schema": "dovetail-release-event/v1",
|
|
1171
|
-
"event_id": "dovetail-webpack-plugin@0.0.10",
|
|
1172
|
-
"package": "@tenonhq/dovetail-webpack-plugin",
|
|
1173
|
-
"version": "0.0.10",
|
|
1174
|
-
"prev_version": "0.0.9",
|
|
1175
|
-
"published_at": "2026-06-05T06:43:18.252Z",
|
|
1176
|
-
"git": {
|
|
1177
|
-
"sha": "71bdf9bba8bd5e3849a212a18d9ebc9241232b32",
|
|
1178
|
-
"tag": "dovetail-webpack-plugin@0.0.10"
|
|
1179
|
-
},
|
|
1180
|
-
"semver_bump": "patch",
|
|
1181
|
-
"commits": [
|
|
1182
|
-
{
|
|
1183
|
-
"sha": "71bdf9bba",
|
|
1184
|
-
"subject": "fix: float internal dep ranges to ~0.0.x so consumers need no overrides (#148)",
|
|
1185
|
-
"pr": 148
|
|
1186
|
-
},
|
|
1187
|
-
{
|
|
1188
|
-
"sha": "9332fdf7a",
|
|
1189
|
-
"subject": "chore(release): publish 20 packages [skip ci]"
|
|
1190
|
-
},
|
|
1191
|
-
{
|
|
1192
|
-
"sha": "9046da774",
|
|
1193
|
-
"subject": "chore(release): publish 19 packages [skip ci]"
|
|
1194
|
-
}
|
|
1195
|
-
],
|
|
1196
|
-
"exports_delta": null,
|
|
1197
|
-
"narrative": null
|
|
1198
|
-
},
|
|
1199
|
-
{
|
|
1200
|
-
"$schema": "dovetail-release-event/v1",
|
|
1201
|
-
"event_id": "dovetail-servicenow@0.0.15",
|
|
1202
|
-
"package": "@tenonhq/dovetail-servicenow",
|
|
1203
|
-
"version": "0.0.15",
|
|
1204
|
-
"prev_version": "0.0.14",
|
|
1205
|
-
"published_at": "2026-06-04T22:16:51.160Z",
|
|
1206
|
-
"git": {
|
|
1207
|
-
"sha": "d46b5bd2dce0fb3b13f0d1d5933f88d1d421b550",
|
|
1208
|
-
"tag": "dovetail-servicenow@0.0.15"
|
|
1209
|
-
},
|
|
1210
|
-
"semver_bump": "patch",
|
|
1211
|
-
"commits": [
|
|
1212
|
-
{
|
|
1213
|
-
"sha": "d46b5bd2d",
|
|
1214
|
-
"subject": "feat(servicenow): dove-sn edit-action — headless action-type script/output edit (#147)",
|
|
1215
|
-
"pr": 147
|
|
1216
|
-
},
|
|
1217
|
-
{
|
|
1218
|
-
"sha": "af1417796",
|
|
1219
|
-
"subject": "chore(release): publish 3 packages [skip ci]"
|
|
1220
|
-
}
|
|
1221
|
-
],
|
|
1222
|
-
"exports_delta": null,
|
|
1223
|
-
"narrative": null
|
|
1224
|
-
},
|
|
1225
|
-
{
|
|
1226
|
-
"$schema": "dovetail-release-event/v1",
|
|
1227
|
-
"event_id": "dovetail-claude-plans@0.0.21",
|
|
1228
|
-
"package": "@tenonhq/dovetail-claude-plans",
|
|
1229
|
-
"version": "0.0.21",
|
|
1230
|
-
"prev_version": "0.0.20",
|
|
1231
|
-
"published_at": "2026-06-04T08:56:53.804Z",
|
|
1232
|
-
"git": {
|
|
1233
|
-
"sha": "44e5c84b85007cf6e9223b279ff79afd5319c5ab",
|
|
1234
|
-
"tag": "dovetail-claude-plans@0.0.21"
|
|
1235
|
-
},
|
|
1236
|
-
"semver_bump": "patch",
|
|
1237
|
-
"commits": [
|
|
1238
|
-
{
|
|
1239
|
-
"sha": "44e5c84b8",
|
|
1240
|
-
"subject": "chore(docs): neutralize upstream operating-repo references in package docs (#145)",
|
|
1241
|
-
"pr": 145
|
|
1242
|
-
},
|
|
1243
|
-
{
|
|
1244
|
-
"sha": "92213e1f8",
|
|
1245
|
-
"subject": "chore(release): publish 2 packages [skip ci]"
|
|
1246
|
-
}
|
|
1247
|
-
],
|
|
1248
|
-
"exports_delta": null,
|
|
1249
|
-
"narrative": null
|
|
1250
|
-
},
|
|
1251
|
-
{
|
|
1252
|
-
"$schema": "dovetail-release-event/v1",
|
|
1253
|
-
"event_id": "dovetail-servicenow@0.0.14",
|
|
1254
|
-
"package": "@tenonhq/dovetail-servicenow",
|
|
1255
|
-
"version": "0.0.14",
|
|
1256
|
-
"prev_version": "0.0.13",
|
|
1257
|
-
"published_at": "2026-06-04T08:56:53.804Z",
|
|
1258
|
-
"git": {
|
|
1259
|
-
"sha": "44e5c84b85007cf6e9223b279ff79afd5319c5ab",
|
|
1260
|
-
"tag": "dovetail-servicenow@0.0.14"
|
|
1261
|
-
},
|
|
1262
|
-
"semver_bump": "patch",
|
|
1263
|
-
"commits": [
|
|
1264
|
-
{
|
|
1265
|
-
"sha": "44e5c84b8",
|
|
1266
|
-
"subject": "chore(docs): neutralize upstream operating-repo references in package docs (#145)",
|
|
1267
|
-
"pr": 145
|
|
1268
|
-
},
|
|
1269
|
-
{
|
|
1270
|
-
"sha": "e0cf83a8c",
|
|
1271
|
-
"subject": "chore(release): @tenonhq/dovetail-servicenow@0.0.13 [skip ci]"
|
|
1272
|
-
}
|
|
1273
|
-
],
|
|
1274
|
-
"exports_delta": null,
|
|
1275
|
-
"narrative": null
|
|
1276
|
-
},
|
|
1277
|
-
{
|
|
1278
|
-
"$schema": "dovetail-release-event/v1",
|
|
1279
|
-
"event_id": "dovetail-mcp@0.0.8",
|
|
1280
|
-
"package": "@tenonhq/dovetail-mcp",
|
|
1281
|
-
"version": "0.0.8",
|
|
1282
|
-
"prev_version": "0.0.7",
|
|
1283
|
-
"published_at": "2026-06-04T08:56:53.804Z",
|
|
1284
|
-
"git": {
|
|
1285
|
-
"sha": "44e5c84b85007cf6e9223b279ff79afd5319c5ab",
|
|
1286
|
-
"tag": "dovetail-mcp@0.0.8"
|
|
1287
|
-
},
|
|
1288
|
-
"semver_bump": "patch",
|
|
1289
|
-
"commits": [
|
|
1290
|
-
{
|
|
1291
|
-
"sha": "44e5c84b8",
|
|
1292
|
-
"subject": "chore(docs): neutralize upstream operating-repo references in package docs (#145)",
|
|
1293
|
-
"pr": 145
|
|
1294
|
-
},
|
|
1295
|
-
{
|
|
1296
|
-
"sha": "bd127c726",
|
|
1297
|
-
"subject": "chore(release): @tenonhq/dovetail-servicenow@0.0.12 [skip ci]"
|
|
1298
|
-
},
|
|
1299
|
-
{
|
|
1300
|
-
"sha": "448696846",
|
|
1301
|
-
"subject": "chore(release): publish 3 packages [skip ci]"
|
|
1302
|
-
}
|
|
1303
|
-
],
|
|
1304
|
-
"exports_delta": null,
|
|
1305
|
-
"narrative": null
|
|
1306
1280
|
}
|
|
1307
1281
|
]
|
|
1308
1282
|
}
|