@shrkcrft/generator 0.1.0-alpha.30 → 0.1.0-alpha.31

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.
@@ -1 +1 @@
1
- {"version":3,"file":"dry-run.d.ts","sourceRoot":"","sources":["../src/dry-run.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAW5D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,eAAe,CAAC;IACtB,gEAAgE;IAChE,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,kBAAkB,GAC1B,aAAa,CA8Gf"}
1
+ {"version":3,"file":"dry-run.d.ts","sourceRoot":"","sources":["../src/dry-run.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAY5D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,eAAe,CAAC;IACtB,gEAAgE;IAChE,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,kBAAkB,GAC1B,aAAa,CAqHf"}
package/dist/dry-run.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { safeResolveTargetPath } from '@shrkcrft/core';
3
- import { renderTemplate, validateTemplateVariables, } from '@shrkcrft/templates';
3
+ import { renderTemplate, templateRemainderLines, validateTemplateVariables, } from '@shrkcrft/templates';
4
4
  import { OverwriteStrategy } from "./overwrite-strategy.js";
5
5
  import { FileChangeType } from "./file-change.js";
6
6
  import { decideForExisting, summarizeConflicts } from "./conflict-handler.js";
7
7
  import { buildNameVariables } from "./naming-strategy.js";
8
8
  import { evaluatePlannedChange, } from "./planned-change.js";
9
+ import { describeInvalidPlannedOperation, validatePlannedOperation } from "./planned-operation-fields.js";
9
10
  export function planGeneration(template, request) {
10
11
  const warnings = [];
11
12
  const overwriteStrategy = request.overwriteStrategy ?? OverwriteStrategy.Never;
@@ -24,6 +25,7 @@ export function planGeneration(template, request) {
24
25
  hasConflicts: false,
25
26
  warnings,
26
27
  postGenerationNotes: template.postGenerationNotes ?? [],
28
+ remainderLines: templateRemainderLines(template),
27
29
  },
28
30
  safe: false,
29
31
  };
@@ -90,10 +92,15 @@ export function planGeneration(template, request) {
90
92
  }
91
93
  // 2) v2 planned changes — evaluate against the live filesystem, threaded
92
94
  // through the overlay so successive changes to one file compose.
93
- for (const tplChange of rendered.changes) {
94
- const evaluated = planOne(tplChange, request.projectRoot, overlay);
95
+ // Each op is shape-checked against PLANNED_OPERATION_FIELDS first, so a
96
+ // misspelled op becomes a located Conflict instead of a TypeError.
97
+ rendered.changes.forEach((tplChange, index) => {
98
+ const evaluated = planOne(tplChange, request.projectRoot, overlay, {
99
+ where: `template '${template.id}' change[${index}]`,
100
+ warnings,
101
+ });
95
102
  changes.push(evaluated);
96
- }
103
+ });
97
104
  const { hasConflicts } = summarizeConflicts(changes);
98
105
  return {
99
106
  plan: {
@@ -104,12 +111,32 @@ export function planGeneration(template, request) {
104
111
  hasConflicts,
105
112
  warnings,
106
113
  postGenerationNotes: rendered.postGenerationNotes,
114
+ remainderLines: templateRemainderLines(template),
107
115
  },
108
116
  safe: !hasConflicts && changes.length > 0,
109
117
  };
110
118
  }
111
- function planOne(tplChange, projectRoot, overlay) {
119
+ function planOne(tplChange, projectRoot, overlay, ctx) {
112
120
  const op = tplChange.operation;
121
+ const where = ctx?.where ?? 'change';
122
+ const shape = validatePlannedOperation(op);
123
+ const rawTarget = typeof tplChange.targetPath === 'string' ? tplChange.targetPath : '';
124
+ const invalid = describeInvalidPlannedOperation(shape, where) ??
125
+ (rawTarget.length === 0 ? `${where} (${shape.kind}): missing targetPath` : undefined);
126
+ if (invalid) {
127
+ return {
128
+ type: FileChangeType.Conflict,
129
+ absolutePath: rawTarget,
130
+ relativePath: rawTarget,
131
+ contents: '',
132
+ reason: invalid,
133
+ sizeBytes: 0,
134
+ operation: op,
135
+ };
136
+ }
137
+ if (shape.unknown.length > 0) {
138
+ ctx?.warnings.push(`${where} (${shape.kind}): unknown key(s) ${shape.unknown.join(', ')} are ignored by the engine`);
139
+ }
113
140
  let safe;
114
141
  try {
115
142
  safe = safeResolveTargetPath(tplChange.targetPath, projectRoot);
@@ -138,12 +165,28 @@ function planOne(tplChange, projectRoot, overlay) {
138
165
  : existsSync(safe.absolutePath)
139
166
  ? readFileSafe(safe.absolutePath)
140
167
  : null;
141
- const result = evaluatePlannedChange({
142
- change,
143
- absolutePath: safe.absolutePath,
144
- relativePath: safe.relativePath,
145
- existing,
146
- });
168
+ let result;
169
+ try {
170
+ result = evaluatePlannedChange({
171
+ change,
172
+ absolutePath: safe.absolutePath,
173
+ relativePath: safe.relativePath,
174
+ existing,
175
+ });
176
+ }
177
+ catch (e) {
178
+ // A well-shaped op can still carry a wrong-typed value; the plan must name
179
+ // the change, never surface a bare TypeError.
180
+ return {
181
+ type: FileChangeType.Conflict,
182
+ absolutePath: safe.absolutePath,
183
+ relativePath: safe.relativePath,
184
+ contents: existing ?? '',
185
+ reason: `${where} (${shape.kind}): could not be evaluated — ${e.message}`,
186
+ sizeBytes: 0,
187
+ operation: op,
188
+ };
189
+ }
147
190
  // Record the cumulative content (Skip/Conflict carry the unchanged bytes,
148
191
  // which is exactly what a later op on the same file should see).
149
192
  overlay?.set(safe.absolutePath, result.contents);
@@ -8,6 +8,13 @@ export interface IGenerationPlan {
8
8
  hasConflicts: boolean;
9
9
  warnings: readonly string[];
10
10
  postGenerationNotes: readonly string[];
11
+ /**
12
+ * The template's declared remainder — what it deliberately does NOT
13
+ * scaffold, and the manual steps that cover it — as the printable lines
14
+ * `templateRemainderLines` renders (`@shrkcrft/templates`). Absent/empty
15
+ * when the template declares neither `notScaffolded` nor `manualSteps`.
16
+ */
17
+ remainderLines?: readonly string[];
11
18
  /**
12
19
  * Optional folder operations attached to the plan. Carried verbatim
13
20
  * into saved plans and executed via `applyFolderOps()` during `shrk apply`
@@ -1 +1 @@
1
- {"version":3,"file":"generation-plan.d.ts","sourceRoot":"","sources":["../src/generation-plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"generation-plan.d.ts","sourceRoot":"","sources":["../src/generation-plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB"}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export * from './naming-strategy.js';
9
9
  export * from './saved-plan.js';
10
10
  export * from './plan-signing.js';
11
11
  export * from './planned-change.js';
12
+ export * from './planned-operation-fields.js';
12
13
  export * from './folder-safety.js';
13
14
  export * from './folder-apply.js';
14
15
  export * from './synthetic-plan.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ export * from "./naming-strategy.js";
9
9
  export * from "./saved-plan.js";
10
10
  export * from "./plan-signing.js";
11
11
  export * from "./planned-change.js";
12
+ export * from "./planned-operation-fields.js";
12
13
  export * from "./folder-safety.js";
13
14
  export * from "./folder-apply.js";
14
15
  export * from "./synthetic-plan.js";
@@ -0,0 +1,71 @@
1
+ export declare const PLANNED_OPERATION_FIELDS: {
2
+ readonly create: {
3
+ readonly required: readonly ["content"];
4
+ readonly optional: readonly ["description"];
5
+ };
6
+ readonly append: {
7
+ readonly required: readonly ["snippet"];
8
+ readonly optional: readonly ["ifMissing", "description"];
9
+ };
10
+ readonly 'insert-after': {
11
+ readonly required: readonly ["anchor", "snippet"];
12
+ readonly optional: readonly ["ifMissing", "description"];
13
+ };
14
+ readonly 'insert-before': {
15
+ readonly required: readonly ["anchor", "snippet"];
16
+ readonly optional: readonly ["ifMissing", "description"];
17
+ };
18
+ readonly replace: {
19
+ readonly required: readonly ["find", "replaceWith"];
20
+ readonly optional: readonly ["expectMatches", "description"];
21
+ };
22
+ readonly export: {
23
+ readonly required: readonly ["from"];
24
+ readonly optional: readonly ["symbols", "ifMissing", "description"];
25
+ };
26
+ readonly 'ensure-import': {
27
+ readonly required: readonly ["from"];
28
+ readonly optional: readonly ["symbols", "typeOnly", "defaultBinding", "namespaceBinding", "description"];
29
+ };
30
+ readonly 'insert-enum-entry': {
31
+ readonly required: readonly ["enumName", "entryName", "entryValue"];
32
+ readonly optional: readonly ["description"];
33
+ };
34
+ readonly 'insert-object-entry': {
35
+ readonly required: readonly ["objectName", "entryKey", "entryValue"];
36
+ readonly optional: readonly ["shorthand", "description"];
37
+ };
38
+ readonly 'insert-array-entry': {
39
+ readonly required: readonly ["arrayName", "entryValue"];
40
+ readonly optional: readonly ["arrayNameAlternatives", "manualStepInstruction", "ifMissing", "description"];
41
+ };
42
+ readonly 'insert-before-closing-brace': {
43
+ readonly required: readonly ["containerName", "snippet"];
44
+ readonly optional: readonly ["ifMissing", "description"];
45
+ };
46
+ readonly 'insert-between-anchors': {
47
+ readonly required: readonly ["beginAnchor", "endAnchor", "snippet"];
48
+ readonly optional: readonly ["ifMissing", "description"];
49
+ };
50
+ };
51
+ /**
52
+ * Check one rendered op against the table. `kindKnown: false` means the kind is
53
+ * not a planned-operation kind at all; `missing` lists required fields that are
54
+ * absent; `unknown` lists keys the evaluator would silently drop; `suggestions`
55
+ * pairs an unknown key with the missing field it most likely meant
56
+ * (`value→entryValue`).
57
+ */
58
+ export declare function validatePlannedOperation(op: unknown): {
59
+ readonly kindKnown: boolean;
60
+ readonly kind: string;
61
+ readonly missing: readonly string[];
62
+ readonly unknown: readonly string[];
63
+ readonly suggestions: readonly string[];
64
+ };
65
+ /**
66
+ * One sentence naming what is wrong with an op, or `undefined` when the op is
67
+ * well-formed (unknown extra keys alone are reported by the caller as a
68
+ * warning, not here).
69
+ */
70
+ export declare function describeInvalidPlannedOperation(shape: ReturnType<typeof validatePlannedOperation>, where: string): string | undefined;
71
+ //# sourceMappingURL=planned-operation-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planned-operation-fields.d.ts","sourceRoot":"","sources":["../src/planned-operation-fields.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BpC,CAAC;AA2BF;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,OAAO,GAAG;IACrD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC,CAuBA;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,EAClD,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,SAAS,CASpB"}
@@ -0,0 +1,100 @@
1
+ export const PLANNED_OPERATION_FIELDS = {
2
+ create: { required: ['content'], optional: ['description'] },
3
+ append: { required: ['snippet'], optional: ['ifMissing', 'description'] },
4
+ 'insert-after': { required: ['anchor', 'snippet'], optional: ['ifMissing', 'description'] },
5
+ 'insert-before': { required: ['anchor', 'snippet'], optional: ['ifMissing', 'description'] },
6
+ replace: { required: ['find', 'replaceWith'], optional: ['expectMatches', 'description'] },
7
+ export: { required: ['from'], optional: ['symbols', 'ifMissing', 'description'] },
8
+ 'ensure-import': {
9
+ required: ['from'],
10
+ optional: ['symbols', 'typeOnly', 'defaultBinding', 'namespaceBinding', 'description'],
11
+ },
12
+ 'insert-enum-entry': { required: ['enumName', 'entryName', 'entryValue'], optional: ['description'] },
13
+ 'insert-object-entry': {
14
+ required: ['objectName', 'entryKey', 'entryValue'],
15
+ optional: ['shorthand', 'description'],
16
+ },
17
+ 'insert-array-entry': {
18
+ required: ['arrayName', 'entryValue'],
19
+ optional: ['arrayNameAlternatives', 'manualStepInstruction', 'ifMissing', 'description'],
20
+ },
21
+ 'insert-before-closing-brace': {
22
+ required: ['containerName', 'snippet'],
23
+ optional: ['ifMissing', 'description'],
24
+ },
25
+ 'insert-between-anchors': {
26
+ required: ['beginAnchor', 'endAnchor', 'snippet'],
27
+ optional: ['ifMissing', 'description'],
28
+ },
29
+ };
30
+ /** Explicit near-misses authors write for a required field. */
31
+ const NEAR_MISS = {
32
+ key: ['entryKey'],
33
+ value: ['entryValue', 'snippet', 'content'],
34
+ name: ['entryName', 'arrayName', 'objectName', 'enumName', 'containerName'],
35
+ body: ['content', 'snippet'],
36
+ text: ['snippet', 'content'],
37
+ code: ['snippet', 'content'],
38
+ snippet: ['content'],
39
+ content: ['snippet'],
40
+ replace: ['replaceWith'],
41
+ with: ['replaceWith'],
42
+ search: ['find'],
43
+ pattern: ['find'],
44
+ begin: ['beginAnchor'],
45
+ end: ['endAnchor'],
46
+ target: ['arrayName', 'objectName', 'enumName', 'containerName'],
47
+ };
48
+ function isPresent(value) {
49
+ if (value === undefined || value === null)
50
+ return false;
51
+ if (typeof value === 'string')
52
+ return true;
53
+ return true;
54
+ }
55
+ /**
56
+ * Check one rendered op against the table. `kindKnown: false` means the kind is
57
+ * not a planned-operation kind at all; `missing` lists required fields that are
58
+ * absent; `unknown` lists keys the evaluator would silently drop; `suggestions`
59
+ * pairs an unknown key with the missing field it most likely meant
60
+ * (`value→entryValue`).
61
+ */
62
+ export function validatePlannedOperation(op) {
63
+ if (!op || typeof op !== 'object') {
64
+ return { kindKnown: false, kind: String(op), missing: [], unknown: [], suggestions: [] };
65
+ }
66
+ const record = op;
67
+ const kind = typeof record.kind === 'string' ? record.kind : String(record.kind);
68
+ const spec = PLANNED_OPERATION_FIELDS[kind];
69
+ if (!spec)
70
+ return { kindKnown: false, kind, missing: [], unknown: [], suggestions: [] };
71
+ const missing = spec.required.filter((f) => !isPresent(record[f]));
72
+ const allowed = new Set(['kind', ...spec.required, ...spec.optional]);
73
+ const unknown = Object.keys(record).filter((k) => !allowed.has(k));
74
+ const suggestions = [];
75
+ for (const u of unknown) {
76
+ const explicit = (NEAR_MISS[u.toLowerCase()] ?? []).filter((f) => missing.includes(f));
77
+ const contained = missing.filter((f) => !explicit.includes(f) && f.toLowerCase().includes(u.toLowerCase()) && u.length >= 3);
78
+ const target = explicit[0] ?? contained[0];
79
+ if (target)
80
+ suggestions.push(`${u}→${target}`);
81
+ }
82
+ return { kindKnown: true, kind, missing, unknown, suggestions };
83
+ }
84
+ /**
85
+ * One sentence naming what is wrong with an op, or `undefined` when the op is
86
+ * well-formed (unknown extra keys alone are reported by the caller as a
87
+ * warning, not here).
88
+ */
89
+ export function describeInvalidPlannedOperation(shape, where) {
90
+ if (!shape.kindKnown) {
91
+ return `${where}: unknown operation kind "${shape.kind}" — must be one of ${Object.keys(PLANNED_OPERATION_FIELDS).join(', ')}`;
92
+ }
93
+ if (shape.missing.length === 0)
94
+ return undefined;
95
+ const parts = [`missing required ${shape.missing.join(', ')}`];
96
+ if (shape.unknown.length > 0)
97
+ parts.push(`unknown keys ${shape.unknown.join(', ')}`);
98
+ const hint = shape.suggestions.length > 0 ? ` — did you mean ${shape.suggestions.join(', ')}?` : '';
99
+ return `${where} (${shape.kind}): ${parts.join('; ')}${hint}`;
100
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shrkcrft/generator",
3
- "version": "0.1.0-alpha.30",
3
+ "version": "0.1.0-alpha.31",
4
4
  "description": "SharkCraft plan-first generator: GenerationPlan, FileChange, dry-run, safe writes.",
5
5
  "license": "MIT",
6
6
  "author": "SharkCraft contributors",
@@ -43,10 +43,10 @@
43
43
  "typecheck": "tsc --noEmit -p tsconfig.json"
44
44
  },
45
45
  "dependencies": {
46
- "@shrkcrft/core": "^0.1.0-alpha.30",
47
- "@shrkcrft/templates": "^0.1.0-alpha.30",
48
- "@shrkcrft/rules": "^0.1.0-alpha.30",
49
- "@shrkcrft/paths": "^0.1.0-alpha.30"
46
+ "@shrkcrft/core": "^0.1.0-alpha.31",
47
+ "@shrkcrft/templates": "^0.1.0-alpha.31",
48
+ "@shrkcrft/rules": "^0.1.0-alpha.31",
49
+ "@shrkcrft/paths": "^0.1.0-alpha.31"
50
50
  },
51
51
  "publishConfig": {
52
52
  "access": "public"