octane 0.13.2 → 0.13.3

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.
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  version: () => version
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const version = "0.13.2";
24
+ const version = "0.13.3";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  version
@@ -3666,7 +3666,10 @@ function autoCalculationOf(expression, selfName, componentLocals, ctx, immutable
3666
3666
  const init = unwrapTsExpr(expression);
3667
3667
  // An arrow/function init is auto-callback's job, not ours.
3668
3668
  if (FN_TYPES.has(init?.type)) return null;
3669
- if (!isPropCreationExpr(init, ctx)) return null;
3669
+ const literal =
3670
+ ctx.strongLiteralMemo === true &&
3671
+ (init?.type === 'ObjectExpression' || init?.type === 'ArrayExpression');
3672
+ if (!isPropCreationExpr(init, ctx, literal)) return null;
3670
3673
  // Every call reached during render must satisfy the same projection contract
3671
3674
  // the region cache uses. Compatibility-mode member calls fail closed.
3672
3675
  const immutableProjection = immutableArrayProjection(init, immutableStates);
@@ -3693,7 +3696,7 @@ function autoCalculationOf(expression, selfName, componentLocals, ctx, immutable
3693
3696
  return null;
3694
3697
  }
3695
3698
  }
3696
- return { init, deps, immutableProjection };
3699
+ return { init, deps, immutableProjection, literal };
3697
3700
  }
3698
3701
 
3699
3702
  // The single-name `const` declarator a calculation can replace, or null.
@@ -3726,6 +3729,8 @@ function closeCalculationReads(
3726
3729
  componentLocals,
3727
3730
  ctx,
3728
3731
  immutableStates = null,
3732
+ literalReads = null,
3733
+ renderRoots = [],
3729
3734
  ) {
3730
3735
  if (renderReadNames.size === 0) return renderReadNames;
3731
3736
  const declaredAt = new Map();
@@ -3750,6 +3755,24 @@ function closeCalculationReads(
3750
3755
  if (decl === undefined) continue;
3751
3756
  const calculation = autoCalculationOf(decl.init, name, componentLocals, ctx, immutableStates);
3752
3757
  if (calculation === null) continue;
3758
+ // An allocated local can still be mutated or escape during setup.
3759
+ // Cache only literals read directly as eligible props/refs; other reads
3760
+ // (including callback captures and alias creation) keep their JS lifetime.
3761
+ if (calculation.literal) {
3762
+ const allowed = literalReads?.get(name);
3763
+ if (
3764
+ !allowed ||
3765
+ statements.some(
3766
+ (statement) =>
3767
+ statement.declarations?.[0] !== decl &&
3768
+ collectFreeIdentifiers(statement, [], allowed).has(name),
3769
+ ) ||
3770
+ collectFreeIdentifiers(renderRoots, [], allowed).has(name)
3771
+ ) {
3772
+ forward.push(name);
3773
+ continue;
3774
+ }
3775
+ }
3753
3776
  const at = declaredAt.get(name);
3754
3777
  if (calculation.deps.some((dependency) => declaredAt.get(dependency) > at)) {
3755
3778
  forward.push(name);
@@ -3782,7 +3805,14 @@ function rewriteAutoCalculation(
3782
3805
  immutableStates,
3783
3806
  );
3784
3807
  if (calculation === null) return stmt;
3785
- const { init, deps, immutableProjection } = calculation;
3808
+ const { init, deps, immutableProjection, literal } = calculation;
3809
+ // An unrelated update may supply a new props bag with the same fields.
3810
+ // Literal identities depend on the inputs they read, not on that bag.
3811
+ const dependencyNodes = literal
3812
+ ? collectDepPaths(init, null, moduleBoundCheckForDeps(ctx, init), () =>
3813
+ requireRuntimeForContext(ctx, METHOD_DEP_IMPORT),
3814
+ )
3815
+ : deps.map((n) => b.id(n));
3786
3816
  return {
3787
3817
  ...stmt,
3788
3818
  declarations: [
@@ -3799,7 +3829,7 @@ function rewriteAutoCalculation(
3799
3829
  : { _octaneImmutableArrayFilter: immutableProjection }),
3800
3830
  },
3801
3831
  b.arrow([], init),
3802
- b.array(deps.map((n) => b.id(n))),
3832
+ b.array(dependencyNodes),
3803
3833
  ),
3804
3834
  init,
3805
3835
  ),
@@ -3843,20 +3873,24 @@ const CALCULATION_VALUE_PROOFS = [
3843
3873
  * unobservable. Positions whose evaluation is conditional or deferred keep
3844
3874
  * their expression: directive arms, component children (the child decides
3845
3875
  * whether and when they render), and built-in boundaries. An expression holding
3846
- * JSX keeps its list and descriptor lowering, and event handlers, refs, and
3847
- * keys keep their wiring. A render tree that writes to anything keeps its whole
3876
+ * JSX keeps its list and descriptor lowering, and event handlers and keys keep
3877
+ * their wiring. Ref arrays use the same identity proof as props. A render tree that writes to anything keeps its whole
3848
3878
  * evaluation order.
3849
3879
  *
3850
3880
  * Returns null when nothing moves; otherwise the copy-on-write render nodes,
3851
3881
  * the declarations to append, and their names.
3852
3882
  */
3853
3883
  function hoistStrongRenderCalculations(jsxNodes, ctx, immutableStates) {
3854
- if (ctx.strongMemo !== true || ctx.nativeReads || !ctx.currentComponentLocals) return null;
3884
+ if ((!ctx.strongMemo && !ctx.strongLiteralMemo) || ctx.nativeReads || !ctx.currentComponentLocals)
3885
+ return null;
3855
3886
  if (renderTreeWrites(jsxNodes)) return null;
3856
3887
  const declarations = [];
3857
3888
  const names = [];
3889
+ const literalReads = new Map();
3858
3890
  const nodes = mapNodes(jsxNodes, visitNode);
3859
- return declarations.length === 0 ? null : { jsxNodes: nodes, declarations, names };
3891
+ return declarations.length === 0 && literalReads.size === 0
3892
+ ? null
3893
+ : { jsxNodes: nodes, declarations, names, literalReads };
3860
3894
 
3861
3895
  function visitNode(node) {
3862
3896
  switch (node?.type) {
@@ -3894,7 +3928,10 @@ function hoistStrongRenderCalculations(jsxNodes, ctx, immutableStates) {
3894
3928
  return attribute;
3895
3929
  }
3896
3930
  const name = attribute.name.name;
3897
- if (name === 'key' || name === 'ref' || name === 'children') return attribute;
3931
+ if (name === 'key' || name === 'children') return attribute;
3932
+ const inner = unwrapTsExpr(attribute.value?.expression);
3933
+ if (name === 'ref' && inner?.type !== 'ArrayExpression' && inner?.type !== 'Identifier')
3934
+ return attribute;
3898
3935
  // The host handoff prepares unbound fields through the ordinary renderer.
3899
3936
  // Hoisting one outside that preparation makes its writer ineligible.
3900
3937
  if (
@@ -3905,7 +3942,7 @@ function hoistStrongRenderCalculations(jsxNodes, ctx, immutableStates) {
3905
3942
  return attribute;
3906
3943
  if (!component && /^on[A-Z]/.test(name)) return attribute;
3907
3944
  if (attribute.value?.type !== 'JSXExpressionContainer') return attribute;
3908
- const value = visitContainer(attribute.value);
3945
+ const value = visitContainer(attribute.value, true);
3909
3946
  return value === attribute.value ? attribute : { ...attribute, value };
3910
3947
  });
3911
3948
  // Component children render inside the child, if at all.
@@ -3918,11 +3955,27 @@ function hoistStrongRenderCalculations(jsxNodes, ctx, immutableStates) {
3918
3955
  };
3919
3956
  }
3920
3957
 
3921
- function visitContainer(container) {
3958
+ function visitContainer(container, prop = false) {
3922
3959
  const expression = container.expression;
3923
3960
  const inner = unwrapTsExpr(expression);
3924
3961
  if (!inner || inner.type === 'JSXEmptyExpression' || containsJsxNode(inner)) return container;
3925
- if (autoCalculationOf(inner, null, ctx.currentComponentLocals, ctx, immutableStates) === null) {
3962
+ if (prop && inner.type === 'Identifier') {
3963
+ let nodes = literalReads.get(inner.name);
3964
+ if (!nodes) literalReads.set(inner.name, (nodes = new Set()));
3965
+ nodes.add(inner);
3966
+ }
3967
+ const calculation = autoCalculationOf(
3968
+ inner,
3969
+ null,
3970
+ ctx.currentComponentLocals,
3971
+ ctx,
3972
+ immutableStates,
3973
+ );
3974
+ if (
3975
+ calculation === null ||
3976
+ (!ctx.strongMemo && !calculation.literal) ||
3977
+ (calculation.literal && !prop)
3978
+ ) {
3926
3979
  return container;
3927
3980
  }
3928
3981
  const name = allocCompilerName(ctx, '__calc');
@@ -3931,6 +3984,7 @@ function hoistStrongRenderCalculations(jsxNodes, ctx, immutableStates) {
3931
3984
  // A child warm plan runs outside this body, so it keeps the authored prop.
3932
3985
  (ctx.hoistedRenderCalculations ??= new Map()).set(name, expression);
3933
3986
  const reference = b.id(name, inner);
3987
+ if (prop) literalReads.set(name, new Set([reference]));
3934
3988
  for (const proof of CALCULATION_VALUE_PROOFS) {
3935
3989
  if (inner.metadata?.[proof] === true) reference.metadata[proof] = true;
3936
3990
  }
@@ -3957,9 +4011,10 @@ function establishesRenderScope(statement) {
3957
4011
  // `return <jsx>` reached after the render-scope proof above gets the same
3958
4012
  // calculations, declared immediately before it. Returns null when nothing moves.
3959
4013
  function hoistStrongReturnCalculations(statements, ctx) {
3960
- if (ctx.strongMemo !== true || ctx.nativeReads) return null;
4014
+ if ((!ctx.strongMemo && !ctx.strongLiteralMemo) || ctx.nativeReads) return null;
3961
4015
  let out = null;
3962
4016
  const names = [];
4017
+ const literalReads = new Map();
3963
4018
  let established = false;
3964
4019
  for (let index = 0; index < statements.length; index++) {
3965
4020
  const statement = statements[index];
@@ -3969,13 +4024,18 @@ function hoistStrongReturnCalculations(statements, ctx) {
3969
4024
  out ??= statements.slice(0, index);
3970
4025
  out.push(...hoisted.declarations, { ...statement, argument: hoisted.jsxNodes[0] });
3971
4026
  names.push(...hoisted.names);
4027
+ for (const [name, reads] of hoisted.literalReads) {
4028
+ let nodes = literalReads.get(name);
4029
+ if (!nodes) literalReads.set(name, (nodes = new Set()));
4030
+ for (const read of reads) nodes.add(read);
4031
+ }
3972
4032
  continue;
3973
4033
  }
3974
4034
  }
3975
4035
  out?.push(statement);
3976
4036
  established ||= establishesRenderScope(statement);
3977
4037
  }
3978
- return out === null ? null : { statements: out, names };
4038
+ return out === null ? null : { statements: out, names, literalReads };
3979
4039
  }
3980
4040
 
3981
4041
  function mapNodes(nodes, map) {
@@ -4928,7 +4988,9 @@ const CALLBACK_IDENTITY_OWNING_HOOKS = new Set(
4928
4988
  );
4929
4989
 
4930
4990
  function hookOwnsCallbackIdentity(call) {
4931
- const name = hookShapedCalleeName(call);
4991
+ // After dependency inference, use the resolved hook even when an import
4992
+ // alias looks like a custom hook. Earlier passes still use the spelling.
4993
+ const name = stableHookCallName(call) ?? hookShapedCalleeName(call);
4932
4994
  return name !== null && CALLBACK_IDENTITY_OWNING_HOOKS.has(name);
4933
4995
  }
4934
4996
 
@@ -11776,6 +11838,14 @@ function compileInternal(
11776
11838
  splitModuleFunctions,
11777
11839
  autoMemo: autoMemoEnabled,
11778
11840
  strongMemo: strongMemoEnabled,
11841
+ // Ref and effect lifetimes must not churn on an unrelated dev/HMR render.
11842
+ // Only proven literal props use these caches; region/call caching stays prod-only.
11843
+ strongLiteralMemo:
11844
+ strongModeEnabled &&
11845
+ options?.autoMemo !== false &&
11846
+ mode === 'client' &&
11847
+ options?.__universal == null &&
11848
+ options?.nativeReads !== true,
11779
11849
  nativeReads: options?.nativeReads === true,
11780
11850
  opaqueSignalHandles:
11781
11851
  options?.opaqueSignalHandles === true || options?.__signalModuleImport === true,
@@ -17892,6 +17962,8 @@ function compileFunctionBody(node, ctx, name, parentNs = 'html', cssHash = null,
17892
17962
  ctx.currentComponentLocals,
17893
17963
  ctx,
17894
17964
  immutableStateProvenance,
17965
+ hoisted?.literalReads,
17966
+ jsxNodes,
17895
17967
  );
17896
17968
  if (renderReadNames.size > 0) {
17897
17969
  workingStatements = workingStatements.map((statement) => {
@@ -19270,8 +19342,8 @@ function isTrivialUseArg(n) {
19270
19342
  // the lowering passes) and hook-shaped calls (`use` / `use[A-Z]…` / a tracked
19271
19343
  // `use` import alias — their slot/occurrence bookkeeping must execute on the
19272
19344
  // body proper every pass, so they can never sit behind a cache hit).
19273
- function isPropCreationExpr(expr, ctx) {
19274
- let found = false;
19345
+ function isPropCreationExpr(expr, ctx, includesLiteral = false) {
19346
+ let found = includesLiteral;
19275
19347
  let blocked = false;
19276
19348
  walk(expr);
19277
19349
  return found && !blocked;
@@ -19305,7 +19377,21 @@ function isPropCreationExpr(expr, ctx) {
19305
19377
  blocked = true;
19306
19378
  return;
19307
19379
  }
19308
- if (FN_TYPES.has(n.type)) return; // does not run during render
19380
+ if (FN_TYPES.has(n.type)) {
19381
+ // A regular function can mutate a containing literal via its receiver,
19382
+ // or retain per-render state on its own function object. Reuse the
19383
+ // callback movement proof before stabilizing either kind of identity.
19384
+ if (
19385
+ includesLiteral &&
19386
+ n.type !== 'ArrowFunctionExpression' &&
19387
+ (!isMovableHookCallback(n) ||
19388
+ hasInlineMemoDirectEval(n) ||
19389
+ (n.id && collectFreeIdentifiers({ ...n, id: null }, []).has(n.id.name)))
19390
+ ) {
19391
+ blocked = true;
19392
+ }
19393
+ return; // does not run during render
19394
+ }
19309
19395
  // Same render-time-call inventory as containsRenderCall: plain and
19310
19396
  // optional calls, constructions, tagged templates, and dynamic
19311
19397
  // import() (ESTree flavor; the Babel flavor is a CallExpression).
@@ -21973,6 +22059,9 @@ function compileReturnJsxFunction(node, ctx, options) {
21973
22059
  collectRenderReadNames(renderedRoots, ctx),
21974
22060
  ctx.currentComponentLocals,
21975
22061
  ctx,
22062
+ null,
22063
+ hoisted?.literalReads,
22064
+ renderedRoots,
21976
22065
  );
21977
22066
  let autoCalculatedDeclarations = null;
21978
22067
  let renderScopeEstablished = false;
@@ -2341,17 +2341,8 @@ export function analyzeStrongHookPolicies(ast, options = {}) {
2341
2341
  for (const { call, scope } of analysis.calls) {
2342
2342
  const name = callNames.get(call);
2343
2343
  const config = DEPENDENCY_HOOKS.get(name);
2344
- if (config === undefined) continue;
2345
- if (name === 'useMemo' || name === 'useCallback') {
2346
- diagnostics.push({
2347
- code: 'OCTANE_STRONG_MANUAL_MEMO',
2348
- node: call,
2349
- hook: name,
2350
- severity: 'error',
2351
- message: `Strong mode owns calculation and callback caching. Replace ${name} with a const declaration and let the compiler track its inputs.`,
2352
- });
2353
- continue;
2354
- }
2344
+ // Authored memo hooks own their dependencies; effect policies do not apply to them.
2345
+ if (config === undefined || name === 'useMemo' || name === 'useCallback') continue;
2355
2346
  const argument = call.arguments[config.deps];
2356
2347
  if (argument === undefined || isUnshadowedUndefined(argument, scope)) {
2357
2348
  // Inference would run this hook on every render, which is the
@@ -131,7 +131,7 @@ export const STRONG_DIAGNOSTICS = [
131
131
  section: 'effects',
132
132
  severity: 'error',
133
133
  detects:
134
- "An explicit dependency argument differs from the compiler's inferred inputs, or cannot be proven equivalent. An array the compiler proves equivalent is reported as a **hint** instead, and its authored behavior is preserved.",
134
+ "An effect or imperative handle dependency argument differs from the compiler's inferred inputs, or cannot be proven equivalent. An array the compiler proves equivalent is reported as a **hint** instead, and its authored behavior is preserved.",
135
135
  replacement: 'Omit the dependency argument.',
136
136
  },
137
137
  {
@@ -139,18 +139,10 @@ export const STRONG_DIAGNOSTICS = [
139
139
  section: 'effects',
140
140
  severity: 'error',
141
141
  detects:
142
- 'A built-in dependency hook receives `null` dependencies, or omits them while its callback reads a binding declared after the hook, or a variable the component assigns after the hook or from a nested function. Inference cannot read that value where the hook is called, so the hook would run on every render.',
142
+ 'An effect or imperative handle receives `null` dependencies, or omits them while its callback reads a binding declared after the hook, or a variable the component assigns after the hook or from a nested function. Inference cannot read that value where the hook is called, so the hook would run on every render.',
143
143
  replacement:
144
144
  'Omit the argument so the compiler tracks reactive inputs. Declare and finish assigning what the hook reads before calling it, or keep a changing value in state or a ref.',
145
145
  },
146
- {
147
- code: 'OCTANE_STRONG_MANUAL_MEMO',
148
- section: 'effects',
149
- severity: 'error',
150
- detects: "A call to Octane's `useMemo` or `useCallback`, including known import aliases.",
151
- replacement:
152
- 'Write a normal calculation or callback declaration and let Strong compilation cache eligible declarations. `octane analyze --fix` rewrites `useMemo(() => value, deps)` to `value` and `useCallback(fn, deps)` to `fn`.',
153
- },
154
146
  {
155
147
  code: 'OCTANE_STRONG_EFFECT_EVENT_DEPENDENCY',
156
148
  section: 'effects',
@@ -533,28 +525,6 @@ export function Label({ text }: { text: string }) {
533
525
  `,
534
526
  note: 'A callback ref runs while the element commits, so a state update there renders the component a second time before paint. `useLayoutSnapshot` reads the element after layout on every commit and re-renders only when the measurement changes. To follow resizes that happen without a commit, attach a `ResizeObserver` from the ref or an effect; its callback may update state.',
535
527
  },
536
- {
537
- id: 'manual-memo',
538
- title: 'Drop useMemo and useCallback',
539
- react: 'const total = useMemo(() => sum(items), [items]);',
540
- strong: 'const total = sum(items);',
541
- codes: ['OCTANE_STRONG_MANUAL_MEMO'],
542
- before: `import { useCallback, useMemo } from 'octane';
543
-
544
- export function Total({ items, onPick }: { items: number[]; onPick: (total: number) => void }) {
545
- const total = useMemo(() => items.reduce((sum, item) => sum + item, 0), [items]);
546
- const pick = useCallback(() => onPick(total), [onPick, total]);
547
- return <button onClick={pick}>{String(total)}</button>;
548
- }
549
- `,
550
- after: `export function Total({ items, onPick }: { items: number[]; onPick: (total: number) => void }) {
551
- const total = items.reduce((sum, item) => sum + item, 0);
552
- const pick = () => onPick(total);
553
- return <button onClick={pick}>{String(total)}</button>;
554
- }
555
- `,
556
- note: 'Strong production builds memoize render calculations from their inputs, and eligible callbacks keep a stable identity in every build until their inferred inputs change. `octane analyze --fix` applies this rewrite.',
557
- },
558
528
  {
559
529
  id: 'latest-ref',
560
530
  title: 'Read the latest props from an effect',
@@ -244,69 +244,6 @@ export function lazyRefSuggestion(idiom, ast, source) {
244
244
  return { ...suggestion, edits };
245
245
  }
246
246
 
247
- const PRIMARY_EXPRESSIONS = new Set([
248
- 'ArrayExpression',
249
- 'CallExpression',
250
- 'Identifier',
251
- 'JSXElement',
252
- 'JSXFragment',
253
- 'Literal',
254
- 'MemberExpression',
255
- 'ParenthesizedExpression',
256
- 'TaggedTemplateExpression',
257
- 'TemplateLiteral',
258
- ]);
259
-
260
- /**
261
- * Replace `useMemo(() => value, deps)` with `value` and
262
- * `useCallback(fn, deps)` with `fn`. A block-bodied memo factory other than a
263
- * single `return` keeps its message only.
264
- *
265
- * @param {any} call
266
- * @param {'useMemo' | 'useCallback'} name
267
- * @param {string} source
268
- */
269
- export function manualMemoSuggestion(call, name, source) {
270
- const callback = unwrap(call.arguments?.[0]);
271
- const message =
272
- name === 'useMemo'
273
- ? 'Write the calculation as a plain expression; Strong compilation caches it.'
274
- : 'Pass the function directly; Strong compilation keeps its identity stable.';
275
- if (callback == null || call.arguments.some((argument) => argument.type === 'SpreadElement')) {
276
- return { message };
277
- }
278
- if (name === 'useCallback') {
279
- return {
280
- message,
281
- edits: [
282
- { start: call.start, end: call.end, text: source.slice(callback.start, callback.end) },
283
- ],
284
- };
285
- }
286
- if (
287
- callback.type !== 'ArrowFunctionExpression' ||
288
- callback.async === true ||
289
- callback.params.length > 0
290
- ) {
291
- return { message };
292
- }
293
- let body = callback.body;
294
- if (body?.type === 'BlockStatement') {
295
- const only = body.body?.length === 1 ? body.body[0] : null;
296
- if (only?.type !== 'ReturnStatement' || only.argument == null) return { message };
297
- body = only.argument;
298
- }
299
- const text = source.slice(body.start, body.end);
300
- if (FUNCTION_SCOPE_WORDS.test(text)) return { message };
301
- // The call was a primary expression, so anything that binds less tightly
302
- // keeps that grouping in parentheses: `useMemo(() => !a, []).b` is `(!a).b`.
303
- const parenthesize = !PRIMARY_EXPRESSIONS.has(body.type);
304
- return {
305
- message,
306
- edits: [{ start: call.start, end: call.end, text: parenthesize ? `(${text})` : text }],
307
- };
308
- }
309
-
310
247
  // Properties and calls that read the committed layout. A state update in an
311
248
  // effect that reads one of these is a measurement, which useLayoutSnapshot owns.
312
249
  const LAYOUT_PROPERTIES = new Set([
@@ -21,7 +21,6 @@ import { createStrongRenderPolicy } from './strong-render-policy.js';
21
21
  import {
22
22
  collectLazyRefIdioms,
23
23
  lazyRefSuggestion,
24
- manualMemoSuggestion,
25
24
  readsLayout,
26
25
  strongDocsUrl,
27
26
  } from './strong-fixes.js';
@@ -6259,12 +6258,8 @@ export function analyzeStrongMode(ast, source, filename, options = {}) {
6259
6258
  ...analyzeStrongExternalStore(ast, strongHookAnalysis, stateDiagnostic),
6260
6259
  );
6261
6260
  for (const policy of analyzeStrongHookPolicies(ast, options)) {
6262
- const suggestions =
6263
- policy.hook === 'useMemo' || policy.hook === 'useCallback'
6264
- ? [manualMemoSuggestion(policy.node, policy.hook, source)]
6265
- : [];
6266
6261
  diagnostics.push({
6267
- ...diagnostic(policy.code, filename, policy.node, policy.message, suggestions),
6262
+ ...diagnostic(policy.code, filename, policy.node, policy.message),
6268
6263
  severity: policy.severity,
6269
6264
  });
6270
6265
  }
@@ -1,4 +1,4 @@
1
- const version = "0.13.2";
1
+ const version = "0.13.3";
2
2
  export {
3
3
  version
4
4
  };
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = "0.13.2";
1
+ const version = "0.13.3";
2
2
  export {
3
3
  version
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octane",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": [