@shrkcrft/boundaries 0.1.0-alpha.26 → 0.1.0-alpha.27

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,4 +1,5 @@
1
1
  import type { IRegistrationIdiom } from '@shrkcrft/core';
2
+ import { type IWiringFileEntry } from './evaluate-wiring.js';
2
3
  export declare const REGISTRATION_GRAPH_SCHEMA: "sharkcraft.registration-graph/v1";
3
4
  /** A token role-site: which idiom + file:line it was found at. */
4
5
  export interface IRegistrationSite {
@@ -54,6 +55,14 @@ export declare function buildRegistrationGraph(projectRoot: string, idioms: read
54
55
  * only (no file reads), so it stays far cheaper than a full build. Never throws.
55
56
  */
56
57
  export declare function registrationGraphSignature(projectRoot: string, idioms: readonly IRegistrationIdiom[], options?: IBuildRegistrationGraphOptions): string;
58
+ /**
59
+ * The set of tokens the given file entries PROVIDE under any idiom. Used to diff
60
+ * a base-ref snapshot against the worktree: a REMOVED provider leaves NO site in
61
+ * the changed file (so post-change site-scoping can't see it), but the base
62
+ * content still shows the token was provided there — so a provider deletion
63
+ * (the most common way DI wiring silently breaks) stays change-attributable.
64
+ */
65
+ export declare function providedTokensFromEntries(idioms: readonly IRegistrationIdiom[], entries: readonly IWiringFileEntry[]): Set<string>;
57
66
  /** A token's full registration chain, with role presence flags. */
58
67
  export interface IRegistrationChain extends IRegistrationNode {
59
68
  readonly isDeclared: boolean;
@@ -71,13 +80,26 @@ export interface IUnprovidedToken {
71
80
  readonly declared: readonly IRegistrationSite[];
72
81
  readonly consumed: readonly IRegistrationSite[];
73
82
  }
83
+ /**
84
+ * Does the changed-file set touch ANY role-site of ANY token in the graph? Lets
85
+ * a changed-only gate tell "evaluated the registration scope, found it clean"
86
+ * (a real pass) apart from "the change touched no wiring at all" (nothing to
87
+ * verify → the caller should skip, never paint green). An empty `changedFiles`
88
+ * is vacuously false.
89
+ */
90
+ export declare function registrationTouchesChanged(graph: IRegistrationGraph, changedFiles: readonly string[]): boolean;
74
91
  /**
75
92
  * `wiring unprovided` — tokens that are DECLARED or CONSUMED but have ZERO
76
93
  * provided sites. This is the silent-at-runtime class: typecheck/AOT-green, but
77
94
  * the provider is never registered (or the injected token has no provider
78
95
  * anywhere), so it resolves to undefined at runtime. The thing imports can't see.
96
+ *
97
+ * When `changedFiles` is supplied the result is CHANGESET-SCOPED: a token is
98
+ * kept only if one of its declared/consumed sites is in the changed set — i.e.
99
+ * "did THIS change leave a token unprovided", the change-attributed question a
100
+ * `wiring unprovided --changed-only` gate needs.
79
101
  */
80
- export declare function registrationUnprovided(graph: IRegistrationGraph): readonly IUnprovidedToken[];
102
+ export declare function registrationUnprovided(graph: IRegistrationGraph, changedFiles?: readonly string[]): readonly IUnprovidedToken[];
81
103
  /** A token provided/registered that nothing consumes — a dead registration. */
82
104
  export interface IOrphanRegistration {
83
105
  readonly token: string;
@@ -87,6 +109,9 @@ export interface IOrphanRegistration {
87
109
  * `wiring orphans` — tokens that ARE provided/registered but have ZERO consumed
88
110
  * sites: a provider/registration nothing injects (a build-clean no-op, or a
89
111
  * sign the consumer was renamed/removed).
112
+ *
113
+ * `changedFiles`, when supplied, scopes the result to orphans whose provided
114
+ * site is in the changed set (the registration THIS change added/touched).
90
115
  */
91
- export declare function registrationOrphans(graph: IRegistrationGraph): readonly IOrphanRegistration[];
116
+ export declare function registrationOrphans(graph: IRegistrationGraph, changedFiles?: readonly string[]): readonly IOrphanRegistration[];
92
117
  //# sourceMappingURL=registration-graph.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registration-graph.d.ts","sourceRoot":"","sources":["../../src/wiring/registration-graph.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAiB,MAAM,gBAAgB,CAAC;AAKxE,eAAO,MAAM,yBAAyB,EAAG,kCAA2C,CAAC;AAErF,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAChD,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAChD,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACjD;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,yBAAyB,CAAC;IAClD,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC9C,6EAA6E;IAC7E,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,8BAA8B;IAC7C,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAiBD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,OAAO,GAAE,8BAAmC,GAC3C,kBAAkB,CAqDpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,SAAS,kBAAkB,EAAE,EACrC,OAAO,GAAE,8BAAmC,GAC3C,MAAM,CAiBR;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,MAAM,GACZ,kBAAkB,GAAG,SAAS,CAShC;AAED,oFAAoF;AACpF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAChD,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACjD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,SAAS,gBAAgB,EAAE,CAI7F;AAED,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACjD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,SAAS,mBAAmB,EAAE,CAI7F"}
1
+ {"version":3,"file":"registration-graph.d.ts","sourceRoot":"","sources":["../../src/wiring/registration-graph.ts"],"names":[],"mappings":"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"}
@@ -86,6 +86,22 @@ export function registrationGraphSignature(projectRoot, idioms, options = {}) {
86
86
  }
87
87
  return createHash('sha1').update(parts.join('\n')).digest('hex').slice(0, 16);
88
88
  }
89
+ /**
90
+ * The set of tokens the given file entries PROVIDE under any idiom. Used to diff
91
+ * a base-ref snapshot against the worktree: a REMOVED provider leaves NO site in
92
+ * the changed file (so post-change site-scoping can't see it), but the base
93
+ * content still shows the token was provided there — so a provider deletion
94
+ * (the most common way DI wiring silently breaks) stays change-attributable.
95
+ */
96
+ export function providedTokensFromEntries(idioms, entries) {
97
+ const tokens = new Set();
98
+ for (const idiom of idioms) {
99
+ const files = entries.filter((f) => matchesAny(f.path, idiom.provided.files));
100
+ for (const s of collectSourceSites(idiom.provided, files).sites)
101
+ tokens.add(s.token);
102
+ }
103
+ return tokens;
104
+ }
89
105
  /**
90
106
  * `wiring chain <token>` — the declared → provided → consumed hops of one token
91
107
  * with file:line at each, or `undefined` if the token is unknown to the graph.
@@ -101,24 +117,57 @@ export function registrationChain(graph, token) {
101
117
  isConsumed: node.consumed.length > 0,
102
118
  };
103
119
  }
120
+ /** Project-relative path normalizer so site files and git changed-files compare. */
121
+ function normalizeRel(p) {
122
+ return p.replace(/\\/g, '/').replace(/^\.\//, '');
123
+ }
124
+ /** True when ANY of `sites` falls in the changed-file scope. */
125
+ function anySiteInScope(sites, scope) {
126
+ return sites.some((s) => scope.has(normalizeRel(s.file)));
127
+ }
128
+ /**
129
+ * Does the changed-file set touch ANY role-site of ANY token in the graph? Lets
130
+ * a changed-only gate tell "evaluated the registration scope, found it clean"
131
+ * (a real pass) apart from "the change touched no wiring at all" (nothing to
132
+ * verify → the caller should skip, never paint green). An empty `changedFiles`
133
+ * is vacuously false.
134
+ */
135
+ export function registrationTouchesChanged(graph, changedFiles) {
136
+ if (changedFiles.length === 0)
137
+ return false;
138
+ const scope = new Set(changedFiles.map(normalizeRel));
139
+ return graph.tokens.some((t) => anySiteInScope([...t.declared, ...t.provided, ...t.consumed], scope));
140
+ }
104
141
  /**
105
142
  * `wiring unprovided` — tokens that are DECLARED or CONSUMED but have ZERO
106
143
  * provided sites. This is the silent-at-runtime class: typecheck/AOT-green, but
107
144
  * the provider is never registered (or the injected token has no provider
108
145
  * anywhere), so it resolves to undefined at runtime. The thing imports can't see.
146
+ *
147
+ * When `changedFiles` is supplied the result is CHANGESET-SCOPED: a token is
148
+ * kept only if one of its declared/consumed sites is in the changed set — i.e.
149
+ * "did THIS change leave a token unprovided", the change-attributed question a
150
+ * `wiring unprovided --changed-only` gate needs.
109
151
  */
110
- export function registrationUnprovided(graph) {
152
+ export function registrationUnprovided(graph, changedFiles) {
153
+ const scope = changedFiles ? new Set(changedFiles.map(normalizeRel)) : undefined;
111
154
  return graph.tokens
112
155
  .filter((t) => t.provided.length === 0 && (t.declared.length > 0 || t.consumed.length > 0))
156
+ .filter((t) => !scope || anySiteInScope([...t.declared, ...t.consumed], scope))
113
157
  .map((t) => ({ token: t.token, declared: t.declared, consumed: t.consumed }));
114
158
  }
115
159
  /**
116
160
  * `wiring orphans` — tokens that ARE provided/registered but have ZERO consumed
117
161
  * sites: a provider/registration nothing injects (a build-clean no-op, or a
118
162
  * sign the consumer was renamed/removed).
163
+ *
164
+ * `changedFiles`, when supplied, scopes the result to orphans whose provided
165
+ * site is in the changed set (the registration THIS change added/touched).
119
166
  */
120
- export function registrationOrphans(graph) {
167
+ export function registrationOrphans(graph, changedFiles) {
168
+ const scope = changedFiles ? new Set(changedFiles.map(normalizeRel)) : undefined;
121
169
  return graph.tokens
122
170
  .filter((t) => t.provided.length > 0 && t.consumed.length === 0)
171
+ .filter((t) => !scope || anySiteInScope(t.provided, scope))
123
172
  .map((t) => ({ token: t.token, provided: t.provided }));
124
173
  }
@@ -7,12 +7,18 @@ export declare const TRACE_DEFAULT_GLOBS: readonly string[];
7
7
  * - `declare` — a canonical definition (`const X = 'lit'`, `kind: 'lit'`, an enum value).
8
8
  * - `register` — added to a collection / mapping (`register('lit')`, an array element, `{ 'lit': … }`).
9
9
  * - `consume` — compared / switched / handled (`=== 'lit'`, `case 'lit':`).
10
+ * - `render` — rendered / emitted at the boundary (JSX child `>{ 'lit' }`, template
11
+ * interpolation `${ 'lit' }`, a `render('lit')` / `res.send('lit')` call).
10
12
  * - `reference`— an occurrence we can't confidently classify (still reported, with context).
13
+ *
14
+ * `render` only ever refines what would otherwise be a `reference`; it never
15
+ * steals a site from `declare` / `register` / `consume`.
11
16
  */
12
17
  export declare enum TraceRole {
13
18
  Declare = "declare",
14
19
  Register = "register",
15
20
  Consume = "consume",
21
+ Render = "render",
16
22
  Reference = "reference"
17
23
  }
18
24
  /** One occurrence of the traced literal (or a const aliased to it). */
@@ -1 +1 @@
1
- {"version":3,"file":"trace-literal.d.ts","sourceRoot":"","sources":["../../src/wiring/trace-literal.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,EAAG,qBAA8B,CAAC;AAE3D,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAShD,CAAC;AAEF;;;;;;;GAOG;AACH,oBAAY,SAAS;IACnB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,SAAS,cAAc;CACxB;AAED,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,YAAY,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAmED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAqFd"}
1
+ {"version":3,"file":"trace-literal.d.ts","sourceRoot":"","sources":["../../src/wiring/trace-literal.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,EAAG,qBAA8B,CAAC;AAE3D,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAShD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,oBAAY,SAAS;IACnB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,SAAS,cAAc;CACxB;AAED,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,YAAY,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,UAAU,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AA6ED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAuFd"}
@@ -17,13 +17,19 @@ export const TRACE_DEFAULT_GLOBS = [
17
17
  * - `declare` — a canonical definition (`const X = 'lit'`, `kind: 'lit'`, an enum value).
18
18
  * - `register` — added to a collection / mapping (`register('lit')`, an array element, `{ 'lit': … }`).
19
19
  * - `consume` — compared / switched / handled (`=== 'lit'`, `case 'lit':`).
20
+ * - `render` — rendered / emitted at the boundary (JSX child `>{ 'lit' }`, template
21
+ * interpolation `${ 'lit' }`, a `render('lit')` / `res.send('lit')` call).
20
22
  * - `reference`— an occurrence we can't confidently classify (still reported, with context).
23
+ *
24
+ * `render` only ever refines what would otherwise be a `reference`; it never
25
+ * steals a site from `declare` / `register` / `consume`.
21
26
  */
22
27
  export var TraceRole;
23
28
  (function (TraceRole) {
24
29
  TraceRole["Declare"] = "declare";
25
30
  TraceRole["Register"] = "register";
26
31
  TraceRole["Consume"] = "consume";
32
+ TraceRole["Render"] = "render";
27
33
  TraceRole["Reference"] = "reference";
28
34
  })(TraceRole || (TraceRole = {}));
29
35
  function escapeRegex(s) {
@@ -89,6 +95,20 @@ function classifyRole(before, after) {
89
95
  if (/\b(kind|type|id|name|slug|key|tag|code|token|permission|route|event|action|channel|topic|status|provide|providerToken)\s*[:=]$/i.test(b)) {
90
96
  return TraceRole.Declare;
91
97
  }
98
+ // Render/handle site — REACHED ONLY after every higher-precision rule above
99
+ // has declined, so this can only refine an occurrence that would otherwise be
100
+ // `reference`; it never steals from declare/register/consume:
101
+ // - JSX child interpolation: `<tag>{ 'lit' }` (before ends `>{`).
102
+ // - template-literal interpolation: `` `${ 'lit' }` `` (before ends `${`).
103
+ // - a render / send-to-client call: `render('lit')`, `res.render('lit')`, `res.send('lit')`.
104
+ if (/>\s*\{$/.test(b))
105
+ return TraceRole.Render;
106
+ if (/\$\{$/.test(b))
107
+ return TraceRole.Render;
108
+ if (/\brender\w*\s*\($/i.test(b))
109
+ return TraceRole.Render;
110
+ if (/\bsend\w*\s*\($/i.test(b))
111
+ return TraceRole.Render;
92
112
  return TraceRole.Reference;
93
113
  }
94
114
  /** A const name is alias-resolvable only if Pascal/SCREAMING-cased (low collision risk). */
@@ -167,6 +187,7 @@ export function traceLiteral(projectRoot, literal, options = {}) {
167
187
  [TraceRole.Declare]: [],
168
188
  [TraceRole.Register]: [],
169
189
  [TraceRole.Consume]: [],
190
+ [TraceRole.Render]: [],
170
191
  [TraceRole.Reference]: [],
171
192
  };
172
193
  for (const s of sites)
@@ -180,6 +201,7 @@ export function traceLiteral(projectRoot, literal, options = {}) {
180
201
  [TraceRole.Declare]: sortSites(byRole[TraceRole.Declare]),
181
202
  [TraceRole.Register]: sortSites(byRole[TraceRole.Register]),
182
203
  [TraceRole.Consume]: sortSites(byRole[TraceRole.Consume]),
204
+ [TraceRole.Render]: sortSites(byRole[TraceRole.Render]),
183
205
  [TraceRole.Reference]: sortSites(byRole[TraceRole.Reference]),
184
206
  },
185
207
  aliases: [...aliasNames].sort(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shrkcrft/boundaries",
3
- "version": "0.1.0-alpha.26",
3
+ "version": "0.1.0-alpha.27",
4
4
  "description": "SharkCraft boundary rules: detect when a repository violates its own architecture (forbidden imports across folder/package/layer boundaries).",
5
5
  "license": "MIT",
6
6
  "author": "SharkCraft contributors",
@@ -43,7 +43,7 @@
43
43
  "typecheck": "tsc --noEmit -p tsconfig.json"
44
44
  },
45
45
  "dependencies": {
46
- "@shrkcrft/core": "^0.1.0-alpha.26"
46
+ "@shrkcrft/core": "^0.1.0-alpha.27"
47
47
  },
48
48
  "publishConfig": {
49
49
  "access": "public"