miniread 1.78.0 → 1.79.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/transforms/_generated/manifest.js +5 -0
- package/dist/transforms/_generated/registry.js +2 -0
- package/dist/transforms/get-binding-stable-start.d.ts +2 -0
- package/dist/transforms/get-binding-stable-start.js +11 -0
- package/dist/transforms/recommended-transform-order.d.ts +1 -1
- package/dist/transforms/recommended-transform-order.js +1 -0
- package/dist/transforms/rename-fs-sync-variables/rename-fs-sync-variables-transform.js +1 -9
- package/dist/transforms/rename-indexeddb-request-variables/analyze-indexeddb-request-binding.d.ts +10 -0
- package/dist/transforms/rename-indexeddb-request-variables/analyze-indexeddb-request-binding.js +86 -0
- package/dist/transforms/rename-indexeddb-request-variables/manifest.json +12 -0
- package/dist/transforms/rename-indexeddb-request-variables/rename-indexeddb-request-variables-transform.d.ts +2 -0
- package/dist/transforms/rename-indexeddb-request-variables/rename-indexeddb-request-variables-transform.js +100 -0
- package/package.json +1 -1
|
@@ -168,6 +168,10 @@ const manifestData = {
|
|
|
168
168
|
recommended: true,
|
|
169
169
|
evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-02-04T12:54:37.434Z", "changedLines": 14, "durationSeconds": 231.553556391, "stableNames": 1358 } },
|
|
170
170
|
},
|
|
171
|
+
"rename-indexeddb-request-variables": {
|
|
172
|
+
recommended: true,
|
|
173
|
+
evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-02-06T14:48:25.555Z", "changedLines": 30, "durationSeconds": 226.960178979, "stableNames": 1358 } },
|
|
174
|
+
},
|
|
171
175
|
"rename-interval-ids": {
|
|
172
176
|
recommended: true,
|
|
173
177
|
evaluations: { "claude-code-2.1.10:claude-code-2.1.11": { "diffSizePercent": 100, "evaluatedAt": "2026-02-01T09:30:55.276Z", "changedLines": 58, "durationSeconds": 201.398360841, "stableNames": 1358 } },
|
|
@@ -460,6 +464,7 @@ export const recommendedTransformIds = [
|
|
|
460
464
|
"rename-fs-sync-variables",
|
|
461
465
|
"rename-http-method-parameters",
|
|
462
466
|
"rename-interval-ids",
|
|
467
|
+
"rename-indexeddb-request-variables",
|
|
463
468
|
"rename-loop-index-variables-v3",
|
|
464
469
|
"rename-loop-length-variables",
|
|
465
470
|
"rename-document-fragment-variables",
|
|
@@ -34,6 +34,7 @@ import { renameFileReaderVariablesTransform } from "../rename-file-reader-variab
|
|
|
34
34
|
import { renameFsSyncVariablesTransform } from "../rename-fs-sync-variables/rename-fs-sync-variables-transform.js";
|
|
35
35
|
import { renameHttpMethodParametersTransform } from "../rename-http-method-parameters/rename-http-method-parameters-transform.js";
|
|
36
36
|
import { renameHttpServerParametersTransform } from "../rename-http-server-parameters/rename-http-server-parameters-transform.js";
|
|
37
|
+
import { renameIndexeddbRequestVariablesTransform } from "../rename-indexeddb-request-variables/rename-indexeddb-request-variables-transform.js";
|
|
37
38
|
import { renameIntervalIdsTransform } from "../rename-interval-ids/rename-interval-ids-transform.js";
|
|
38
39
|
import { renameInvalidParameterArgumentsTransform } from "../rename-invalid-parameter-arguments/rename-invalid-parameter-arguments-transform.js";
|
|
39
40
|
import { renameLoopIndexVariablesTransform } from "../rename-loop-index-variables/rename-loop-index-variables-transform.js";
|
|
@@ -118,6 +119,7 @@ export const transformRegistry = {
|
|
|
118
119
|
[renameFsSyncVariablesTransform.id]: renameFsSyncVariablesTransform,
|
|
119
120
|
[renameHttpMethodParametersTransform.id]: renameHttpMethodParametersTransform,
|
|
120
121
|
[renameHttpServerParametersTransform.id]: renameHttpServerParametersTransform,
|
|
122
|
+
[renameIndexeddbRequestVariablesTransform.id]: renameIndexeddbRequestVariablesTransform,
|
|
121
123
|
[renameIntervalIdsTransform.id]: renameIntervalIdsTransform,
|
|
122
124
|
[renameInvalidParameterArgumentsTransform.id]: renameInvalidParameterArgumentsTransform,
|
|
123
125
|
[renameLoopIndexVariablesTransform.id]: renameLoopIndexVariablesTransform,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const BINDING_INDEX_SCALE = 1_000_000;
|
|
2
|
+
export const getBindingStableStart = (binding) => {
|
|
3
|
+
const start = binding.identifier.start;
|
|
4
|
+
if (typeof start === "number")
|
|
5
|
+
return start;
|
|
6
|
+
const location = binding.identifier.loc;
|
|
7
|
+
if (location) {
|
|
8
|
+
return location.start.line * BINDING_INDEX_SCALE + location.start.column;
|
|
9
|
+
}
|
|
10
|
+
return Number.MAX_SAFE_INTEGER;
|
|
11
|
+
};
|
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* This lets us tune transform interactions intentionally instead of relying on
|
|
5
5
|
* alphabetical ID sorting.
|
|
6
6
|
*/
|
|
7
|
-
export declare const recommendedTransformOrder: readonly ["stabilize-top-level-bindings", "stabilize-nested-bindings", "stabilize-deferred-top-level-bindings", "stabilize-deferred-stable-rhs", "expand-boolean-literals", "expand-sequence-expressions-v5", "expand-special-number-literals", "expand-typeof-undefined-comparisons", "expand-undefined-literals", "remove-redundant-else", "rename-arguments-length-flags", "rename-asap-wrappers", "rename-awaiter-parameters", "rename-awaiter-helper-functions", "rename-buffer-variables", "rename-to-buffer-results", "rename-catch-parameters", "rename-promise-catch-parameters", "rename-char-code-at", "rename-charcode-variables-v2", "rename-client-aliases", "rename-comparison-flags", "rename-date-now-start-times", "rename-default-options-parameters-v2", "rename-deferred-resolve-parameters", "rename-destructured-aliases", "rename-rest-parameters", "rename-execfile-arguments", "rename-file-reader-variables", "rename-error-first-callback-parameters", "rename-error-first-callback-parameters-v2", "rename-error-variables", "rename-event-parameters", "rename-http-server-parameters", "rename-fs-sync-variables", "rename-http-method-parameters", "rename-interval-ids", "rename-loop-index-variables-v3", "rename-loop-length-variables", "rename-document-fragment-variables", "rename-object-keys-variables", "rename-object-keys-iterator-variables", "rename-parameters-to-match-properties-v2", "rename-invalid-parameter-arguments", "rename-promise-executor-parameters-v2", "rename-range-parameters", "rename-read-file-lines", "rename-regex-builders", "rename-regex-source-parameters", "rename-search-parameters-variables", "rename-file-extension-variables", "rename-string-split-variables", "rename-this-aliases", "rename-timeout-promises", "rename-timeout-ids", "rename-typeof-variables", "rename-typescript-helper-aliases", "rename-uint8array-concat-variables", "rename-url-parameters", "rename-url-variables", "rename-use-reference-guards-v2", "rename-use-reference-sets", "rename-worker-handles", "rename-zod-check-parameters", "simplify-boolean-negations", "simplify-string-trim", "split-variable-declarations", "use-optional-chaining", "use-object-property-shorthand", "use-object-shorthand", "replace-dynamic-require-eval"];
|
|
7
|
+
export declare const recommendedTransformOrder: readonly ["stabilize-top-level-bindings", "stabilize-nested-bindings", "stabilize-deferred-top-level-bindings", "stabilize-deferred-stable-rhs", "expand-boolean-literals", "expand-sequence-expressions-v5", "expand-special-number-literals", "expand-typeof-undefined-comparisons", "expand-undefined-literals", "remove-redundant-else", "rename-arguments-length-flags", "rename-asap-wrappers", "rename-awaiter-parameters", "rename-awaiter-helper-functions", "rename-buffer-variables", "rename-to-buffer-results", "rename-catch-parameters", "rename-promise-catch-parameters", "rename-char-code-at", "rename-charcode-variables-v2", "rename-client-aliases", "rename-comparison-flags", "rename-date-now-start-times", "rename-default-options-parameters-v2", "rename-deferred-resolve-parameters", "rename-destructured-aliases", "rename-rest-parameters", "rename-execfile-arguments", "rename-file-reader-variables", "rename-error-first-callback-parameters", "rename-error-first-callback-parameters-v2", "rename-error-variables", "rename-event-parameters", "rename-http-server-parameters", "rename-fs-sync-variables", "rename-http-method-parameters", "rename-interval-ids", "rename-indexeddb-request-variables", "rename-loop-index-variables-v3", "rename-loop-length-variables", "rename-document-fragment-variables", "rename-object-keys-variables", "rename-object-keys-iterator-variables", "rename-parameters-to-match-properties-v2", "rename-invalid-parameter-arguments", "rename-promise-executor-parameters-v2", "rename-range-parameters", "rename-read-file-lines", "rename-regex-builders", "rename-regex-source-parameters", "rename-search-parameters-variables", "rename-file-extension-variables", "rename-string-split-variables", "rename-this-aliases", "rename-timeout-promises", "rename-timeout-ids", "rename-typeof-variables", "rename-typescript-helper-aliases", "rename-uint8array-concat-variables", "rename-url-parameters", "rename-url-variables", "rename-use-reference-guards-v2", "rename-use-reference-sets", "rename-worker-handles", "rename-zod-check-parameters", "simplify-boolean-negations", "simplify-string-trim", "split-variable-declarations", "use-optional-chaining", "use-object-property-shorthand", "use-object-shorthand", "replace-dynamic-require-eval"];
|
|
@@ -54,6 +54,7 @@ export const recommendedTransformOrder = [
|
|
|
54
54
|
"rename-fs-sync-variables",
|
|
55
55
|
"rename-http-method-parameters",
|
|
56
56
|
"rename-interval-ids",
|
|
57
|
+
"rename-indexeddb-request-variables",
|
|
57
58
|
"rename-loop-index-variables-v3",
|
|
58
59
|
"rename-loop-length-variables",
|
|
59
60
|
"rename-document-fragment-variables",
|
|
@@ -2,6 +2,7 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { collectExportedNames } from "../../core/collect-exported-names.js";
|
|
3
3
|
import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
|
|
4
4
|
import { getFilesToProcess, } from "../../core/types.js";
|
|
5
|
+
import { getBindingStableStart } from "../get-binding-stable-start.js";
|
|
5
6
|
import { hasDynamicNameLookup } from "../has-dynamic-name-lookup.js";
|
|
6
7
|
import { isNodeFileSystemNamespaceBinding } from "../is-node-file-system-identifier.js";
|
|
7
8
|
const require = createRequire(import.meta.url);
|
|
@@ -56,15 +57,6 @@ const shouldRename = (identifier) => {
|
|
|
56
57
|
return false;
|
|
57
58
|
return true;
|
|
58
59
|
};
|
|
59
|
-
const getBindingStableStart = (binding) => {
|
|
60
|
-
const start = binding.identifier.start;
|
|
61
|
-
if (typeof start === "number")
|
|
62
|
-
return start;
|
|
63
|
-
const loc = binding.identifier.loc;
|
|
64
|
-
if (loc)
|
|
65
|
-
return loc.start.line * 1_000_000 + loc.start.column;
|
|
66
|
-
return Number.MAX_SAFE_INTEGER;
|
|
67
|
-
};
|
|
68
60
|
export const renameFsSyncVariablesTransform = {
|
|
69
61
|
id: "rename-fs-sync-variables",
|
|
70
62
|
description: "Renames Node fs bindings used with 2+ distinct fs.*Sync methods to stable fs-based names",
|
package/dist/transforms/rename-indexeddb-request-variables/analyze-indexeddb-request-binding.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Binding } from "@babel/traverse";
|
|
2
|
+
import type { CallExpression, OptionalCallExpression } from "@babel/types";
|
|
3
|
+
type RequestMethodName = "open" | "deleteDatabase";
|
|
4
|
+
type RequestUsage = {
|
|
5
|
+
properties: Set<string>;
|
|
6
|
+
};
|
|
7
|
+
export declare const getIndexedDatabaseRequestInitMethod: (init: CallExpression | OptionalCallExpression) => RequestMethodName | undefined;
|
|
8
|
+
export declare const getIndexedDBRequestUsage: (binding: Binding) => RequestUsage | undefined;
|
|
9
|
+
export declare const hasOpenRequestSpecificSignals: (properties: Set<string>) => boolean;
|
|
10
|
+
export {};
|
package/dist/transforms/rename-indexeddb-request-variables/analyze-indexeddb-request-binding.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const REQUEST_MEMBER_PROPERTIES = new Set([
|
|
2
|
+
"onerror",
|
|
3
|
+
"onsuccess",
|
|
4
|
+
"onblocked",
|
|
5
|
+
"onupgradeneeded",
|
|
6
|
+
"result",
|
|
7
|
+
"error",
|
|
8
|
+
"transaction",
|
|
9
|
+
]);
|
|
10
|
+
const OPEN_ONLY_SPECIFIC_PROPERTIES = new Set([
|
|
11
|
+
"onblocked",
|
|
12
|
+
"onupgradeneeded",
|
|
13
|
+
"transaction",
|
|
14
|
+
]);
|
|
15
|
+
const REQUEST_EVENT_HANDLER_PROPERTIES = new Set([
|
|
16
|
+
"onsuccess",
|
|
17
|
+
"onerror",
|
|
18
|
+
"onblocked",
|
|
19
|
+
"onupgradeneeded",
|
|
20
|
+
]);
|
|
21
|
+
const REQUEST_VALUE_PROPERTIES = new Set(["result", "error"]);
|
|
22
|
+
const getMemberPropertyName = (member) => {
|
|
23
|
+
if (member.computed) {
|
|
24
|
+
if (member.property.type !== "StringLiteral")
|
|
25
|
+
return;
|
|
26
|
+
return member.property.value;
|
|
27
|
+
}
|
|
28
|
+
if (member.property.type !== "Identifier")
|
|
29
|
+
return;
|
|
30
|
+
return member.property.name;
|
|
31
|
+
};
|
|
32
|
+
export const getIndexedDatabaseRequestInitMethod = (init) => {
|
|
33
|
+
const callee = init.callee;
|
|
34
|
+
if (callee.type !== "MemberExpression" &&
|
|
35
|
+
callee.type !== "OptionalMemberExpression") {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const propertyName = getMemberPropertyName(callee);
|
|
39
|
+
if (!propertyName)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (propertyName === "open")
|
|
42
|
+
return "open";
|
|
43
|
+
// `deleteDatabase` is distinctive enough that we intentionally avoid
|
|
44
|
+
// receiver-origin checks to preserve support for aliased IDB factories.
|
|
45
|
+
if (propertyName === "deleteDatabase")
|
|
46
|
+
return "deleteDatabase";
|
|
47
|
+
return undefined;
|
|
48
|
+
};
|
|
49
|
+
export const getIndexedDBRequestUsage = (binding) => {
|
|
50
|
+
const properties = new Set();
|
|
51
|
+
for (const referencePath of binding.referencePaths) {
|
|
52
|
+
// Be conservative: if any reference is not a direct request member access,
|
|
53
|
+
// skip renaming to avoid mislabeling generic `.open()` call results.
|
|
54
|
+
if (!referencePath.isIdentifier())
|
|
55
|
+
return;
|
|
56
|
+
const parentPath = referencePath.parentPath;
|
|
57
|
+
if (!parentPath.isMemberExpression() &&
|
|
58
|
+
!parentPath.isOptionalMemberExpression()) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const memberExpression = parentPath.node;
|
|
62
|
+
if (memberExpression.object !== referencePath.node)
|
|
63
|
+
return;
|
|
64
|
+
const propertyName = getMemberPropertyName(memberExpression);
|
|
65
|
+
if (!propertyName)
|
|
66
|
+
return;
|
|
67
|
+
if (!REQUEST_MEMBER_PROPERTIES.has(propertyName))
|
|
68
|
+
return;
|
|
69
|
+
properties.add(propertyName);
|
|
70
|
+
}
|
|
71
|
+
return { properties };
|
|
72
|
+
};
|
|
73
|
+
const hasAnyProperty = (properties, allowedProperties) => {
|
|
74
|
+
for (const property of properties) {
|
|
75
|
+
if (allowedProperties.has(property))
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
};
|
|
80
|
+
export const hasOpenRequestSpecificSignals = (properties) =>
|
|
81
|
+
// `open()` is generic across APIs, so we require either IndexedDB-specific
|
|
82
|
+
// signals (`onblocked`/`onupgradeneeded`/`transaction`) or the paired
|
|
83
|
+
// request pattern (event callback + `result`/`error`) used by IDB requests.
|
|
84
|
+
hasAnyProperty(properties, OPEN_ONLY_SPECIFIC_PROPERTIES) ||
|
|
85
|
+
(hasAnyProperty(properties, REQUEST_EVENT_HANDLER_PROPERTIES) &&
|
|
86
|
+
hasAnyProperty(properties, REQUEST_VALUE_PROPERTIES));
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { getIndexedDatabaseRequestInitMethod, getIndexedDBRequestUsage, hasOpenRequestSpecificSignals, } from "./analyze-indexeddb-request-binding.js";
|
|
3
|
+
import { collectExportedNames } from "../../core/collect-exported-names.js";
|
|
4
|
+
import { isStableRenamed, RenameGroup } from "../../core/stable-naming.js";
|
|
5
|
+
import { getFilesToProcess, } from "../../core/types.js";
|
|
6
|
+
import { getBindingStableStart } from "../get-binding-stable-start.js";
|
|
7
|
+
import { hasDynamicNameLookup } from "../has-dynamic-name-lookup.js";
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
10
|
+
const traverse = require("@babel/traverse").default;
|
|
11
|
+
const BASE_NAME = "dbRequest";
|
|
12
|
+
const getRequestMethod = (init) => {
|
|
13
|
+
if (init?.type !== "CallExpression" &&
|
|
14
|
+
init?.type !== "OptionalCallExpression") {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
return getIndexedDatabaseRequestInitMethod(init);
|
|
18
|
+
};
|
|
19
|
+
export const renameIndexeddbRequestVariablesTransform = {
|
|
20
|
+
id: "rename-indexeddb-request-variables",
|
|
21
|
+
description: "Renames IndexedDB open/deleteDatabase request bindings to stable dbRequest names based on request event usage",
|
|
22
|
+
scope: "file",
|
|
23
|
+
parallelizable: true,
|
|
24
|
+
transform(context) {
|
|
25
|
+
let nodesVisited = 0;
|
|
26
|
+
let transformationsApplied = 0;
|
|
27
|
+
for (const fileInfo of getFilesToProcess(context)) {
|
|
28
|
+
if (hasDynamicNameLookup(fileInfo.ast))
|
|
29
|
+
continue;
|
|
30
|
+
const renameCandidatesByScope = new Map();
|
|
31
|
+
const isScript = fileInfo.ast.program.sourceType === "script";
|
|
32
|
+
const exportedNames = collectExportedNames(fileInfo.ast.program);
|
|
33
|
+
traverse(fileInfo.ast, {
|
|
34
|
+
VariableDeclarator(path) {
|
|
35
|
+
nodesVisited++;
|
|
36
|
+
const id = path.node.id;
|
|
37
|
+
if (id.type !== "Identifier")
|
|
38
|
+
return;
|
|
39
|
+
if (isStableRenamed(id.name))
|
|
40
|
+
return;
|
|
41
|
+
if (id.name.toLowerCase().includes("request"))
|
|
42
|
+
return;
|
|
43
|
+
const initMethod = getRequestMethod(path.node.init);
|
|
44
|
+
if (!initMethod)
|
|
45
|
+
return;
|
|
46
|
+
const binding = path.scope.getBinding(id.name);
|
|
47
|
+
if (!binding)
|
|
48
|
+
return;
|
|
49
|
+
if (!binding.constant)
|
|
50
|
+
return;
|
|
51
|
+
if (binding.referencePaths.length === 0)
|
|
52
|
+
return;
|
|
53
|
+
if (isScript && binding.scope.block.type === "Program")
|
|
54
|
+
return;
|
|
55
|
+
if (binding.scope.block.type === "Program" &&
|
|
56
|
+
exportedNames.has(id.name)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const usage = getIndexedDBRequestUsage(binding);
|
|
60
|
+
if (!usage)
|
|
61
|
+
return;
|
|
62
|
+
if (usage.properties.size < 2)
|
|
63
|
+
return;
|
|
64
|
+
if (initMethod === "open" &&
|
|
65
|
+
!hasOpenRequestSpecificSignals(usage.properties)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const existing = renameCandidatesByScope.get(binding.scope);
|
|
69
|
+
if (existing) {
|
|
70
|
+
existing.push(binding);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
renameCandidatesByScope.set(binding.scope, [binding]);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
for (const [scope, bindings] of renameCandidatesByScope) {
|
|
78
|
+
bindings.sort((a, b) => {
|
|
79
|
+
const aStart = getBindingStableStart(a);
|
|
80
|
+
const bStart = getBindingStableStart(b);
|
|
81
|
+
if (aStart !== bStart)
|
|
82
|
+
return aStart - bStart;
|
|
83
|
+
return a.identifier.name.localeCompare(b.identifier.name);
|
|
84
|
+
});
|
|
85
|
+
for (const binding of bindings) {
|
|
86
|
+
// Apply per binding to force stable `$dbRequest` slots (`$dbRequest`,
|
|
87
|
+
// `$dbRequest2`, ...) even when multiple request bindings share scope.
|
|
88
|
+
const group = new RenameGroup();
|
|
89
|
+
group.add({
|
|
90
|
+
scope,
|
|
91
|
+
currentName: binding.identifier.name,
|
|
92
|
+
baseName: BASE_NAME,
|
|
93
|
+
});
|
|
94
|
+
transformationsApplied += group.apply();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return Promise.resolve({ nodesVisited, transformationsApplied });
|
|
99
|
+
},
|
|
100
|
+
};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "miniread",
|
|
3
3
|
"author": "Łukasz Jerciński",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.79.0",
|
|
6
6
|
"description": "Transform minified JavaScript/TypeScript into a more readable form using deterministic AST-based transforms.",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|