@absolutejs/absolute 0.20.0-beta.170 → 0.20.0-beta.172

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.
Files changed (143) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +33 -4
  3. package/changelog.json +128 -0
  4. package/dist/angular/browser.js +2 -2
  5. package/dist/angular/browser.js.map +3 -3
  6. package/dist/angular/components/constants.js +1 -0
  7. package/dist/angular/components/core/streamingSlotRegistrar.js +2 -2
  8. package/dist/angular/components/core/streamingSlotRegistry.js +3 -3
  9. package/dist/angular/index.js +184 -29
  10. package/dist/angular/index.js.map +15 -14
  11. package/dist/angular/server.js +184 -29
  12. package/dist/angular/server.js.map +15 -14
  13. package/dist/build.js +1241 -853
  14. package/dist/build.js.map +32 -30
  15. package/dist/cli/{add-6a7pjkn9.js → add-ae6mg79m.js} +10 -10
  16. package/dist/cli/{analyze-606fshwa.js → analyze-e57zz9x8.js} +3 -3
  17. package/dist/cli/{androidEmulatorController-68jjdxmx.js → androidEmulatorController-vqc5m2rp.js} +6 -6
  18. package/dist/cli/{androidNativeWatcher-x032x9yr.js → androidNativeWatcher-s03aczxv.js} +6 -6
  19. package/dist/cli/{api-mshjr4ah.js → api-8ppxw487.js} +8 -8
  20. package/dist/cli/{build-g81xqbb9.js → build-fz7qbdn3.js} +3 -3
  21. package/dist/cli/{buildDepVendor-29d8v9cx.js → buildDepVendor-qvsh6dsj.js} +7 -3
  22. package/dist/cli/{compile-9y1w9zpj.js → compile-ndc36zna.js} +10 -7
  23. package/dist/cli/config/server.js +36 -6
  24. package/dist/cli/{db-5b4s1kvx.js → db-qbzjw3en.js} +2 -2
  25. package/dist/cli/{dev-hyeb89hp.js → dev-x49583aj.js} +43 -15
  26. package/dist/cli/{doctor-h55y9hj1.js → doctor-repprx92.js} +42 -12
  27. package/dist/cli/{emulatorDoctor-p3yt7140.js → emulatorDoctor-s1z5tkp7.js} +5 -5
  28. package/dist/cli/{emulatorInstaller-4pm2kx9w.js → emulatorInstaller-pgkdswvq.js} +6 -6
  29. package/dist/cli/{env-snb0597q.js → env-5nz1qm8e.js} +3 -3
  30. package/dist/cli/{expoDevController-0vj8dvkh.js → expoDevController-018anvbm.js} +6 -6
  31. package/dist/cli/{generate-20x1pkf7.js → generate-brvjesd6.js} +8 -8
  32. package/dist/cli/{heapDiff-wqj8vrq8.js → heapDiff-kncvdtn8.js} +3 -3
  33. package/dist/cli/htmx/htmx.min.js +1 -1
  34. package/dist/cli/{htmx-6mz2h7k1.js → htmx-5twhjz7p.js} +7 -7
  35. package/dist/cli/{index-w09fzs9f.js → index-0z5fgb80.js} +1 -1
  36. package/dist/cli/{index-exfkpjja.js → index-125cfzvn.js} +2 -2
  37. package/dist/cli/{index-32ge2w1f.js → index-25e7k849.js} +16 -8
  38. package/dist/cli/{index-ys2rzqhg.js → index-2shf4aya.js} +1 -1
  39. package/dist/cli/{index-74m7kh8c.js → index-3ewt17wm.js} +1 -1
  40. package/dist/cli/{index-zd3cna19.js → index-3sfcmb49.js} +1 -1
  41. package/dist/cli/{index-14fbvz34.js → index-50ag35xg.js} +2 -2
  42. package/dist/cli/{index-h5qx5jpk.js → index-7854xf0m.js} +1 -1
  43. package/dist/cli/{index-3y57kd7x.js → index-8khbn8qr.js} +3 -3
  44. package/dist/cli/{index-xw1byvzp.js → index-9djrap79.js} +1 -1
  45. package/dist/cli/{index-ppphkyps.js → index-a6xx0p9e.js} +1 -1
  46. package/dist/cli/{index-g4ehq6ev.js → index-aemhvvwb.js} +1 -1
  47. package/dist/cli/{index-z6f43b20.js → index-b1wht4q5.js} +1 -1
  48. package/dist/cli/{index-s3xvccq3.js → index-btp4fyf1.js} +2 -2
  49. package/dist/cli/{index-066qhc6n.js → index-cgmp9d02.js} +1 -1
  50. package/dist/cli/{index-3wz3mktm.js → index-gyjxrnnz.js} +1 -1
  51. package/dist/cli/index-jwfambwe.js +13 -0
  52. package/dist/cli/{index-3wm8p7rg.js → index-n6faa3m3.js} +1 -1
  53. package/dist/cli/{index-kye08pjx.js → index-pr1skxen.js} +1 -1
  54. package/dist/cli/{index-t8cf0zj9.js → index-prza6tkp.js} +3 -3
  55. package/dist/cli/{index-qbrzvmh9.js → index-q3snnhyd.js} +1 -1
  56. package/dist/cli/{index-3vmc30gf.js → index-rz56x932.js} +1 -1
  57. package/dist/cli/{index-9sksd8c8.js → index-vh68jb0e.js} +2 -2
  58. package/dist/cli/index.js +32 -32
  59. package/dist/cli/{info-mn125212.js → info-k124sh7g.js} +4 -4
  60. package/dist/cli/{inspect-c7s117j0.js → inspect-n6pcfvfw.js} +5 -5
  61. package/dist/cli/{iosNativeWatcher-0nm6qad5.js → iosNativeWatcher-wdq2fvj1.js} +7 -7
  62. package/dist/cli/{iosSimulatorController-hza2c3rv.js → iosSimulatorController-n97dpa8p.js} +7 -7
  63. package/dist/cli/{islands-dc54e5y1.js → islands-p4vgx7a2.js} +3 -3
  64. package/dist/cli/{logs-ej1add2b.js → logs-shg23fbt.js} +3 -3
  65. package/dist/cli/{ls-b6zz24nt.js → ls-0hfx323m.js} +3 -3
  66. package/dist/cli/{mem-5q7n3ymb.js → mem-16sy1707.js} +5 -5
  67. package/dist/cli/{migrate-6nm4kdf6.js → migrate-k91k050s.js} +2 -2
  68. package/dist/cli/{mobile-ajnnvqtv.js → mobile-nqdkk3gd.js} +13 -13
  69. package/dist/cli/prerender-010z771p.js +10 -0
  70. package/dist/cli/{prettier-phdtvqef.js → prettier-dh5fh786.js} +1 -1
  71. package/dist/cli/{ps-ezmzyscn.js → ps-pk7ncjac.js} +6 -6
  72. package/dist/cli/{psTui-3x3ykjve.js → psTui-50wf0b72.js} +7 -7
  73. package/dist/cli/{remoteMacProtocol-g09myqt5.js → remoteMacProtocol-g10s408w.js} +7 -7
  74. package/dist/cli/{remove-t8x1mrxg.js → remove-abaf0w6k.js} +8 -8
  75. package/dist/cli/{routes-3xj256gx.js → routes-mc3ymp8t.js} +3 -3
  76. package/dist/cli/{start-d6p831ny.js → start-t3cq57wn.js} +5 -5
  77. package/dist/cli/{typecheck-stbt0qt5.js → typecheck-fag7djt5.js} +38 -10
  78. package/dist/cli/{workspace-61hh6jq1.js → workspace-7sbyyc59.js} +5 -5
  79. package/dist/client/chunks/islands-s2ta5ajb.js.map +1 -1
  80. package/dist/client/navigation.js +2 -1
  81. package/dist/client/navigation.js.map +3 -3
  82. package/dist/dev/client/domTracker.ts +52 -19
  83. package/dist/dev/client/frameworkDetect.ts +11 -0
  84. package/dist/dev/client/handlers/html.ts +6 -1
  85. package/dist/dev/client/handlers/htmx.ts +6 -1
  86. package/dist/dev/client/hmrClient.ts +10 -0
  87. package/dist/dev/serverBootstrap.js +112 -6
  88. package/dist/index.js +1147 -757
  89. package/dist/index.js.map +32 -30
  90. package/dist/islands/index.js +16 -5
  91. package/dist/islands/index.js.map +6 -6
  92. package/dist/mobile/index.js +2 -2
  93. package/dist/mobile/index.js.map +3 -3
  94. package/dist/react/components/index.js +2 -2
  95. package/dist/react/components/index.js.map +3 -3
  96. package/dist/react/index.js +85 -18
  97. package/dist/react/index.js.map +11 -11
  98. package/dist/react/server.js +71 -15
  99. package/dist/react/server.js.map +8 -8
  100. package/dist/src/angular/baseHref.d.ts +2 -0
  101. package/dist/src/angular/components/constants.d.ts +1 -0
  102. package/dist/src/angular/ssrRender.d.ts +1 -1
  103. package/dist/src/build/compileAngular.d.ts +15 -0
  104. package/dist/src/build/resolveBuildMode.d.ts +6 -0
  105. package/dist/src/build/scanAngularHandlerCalls.d.ts +3 -1
  106. package/dist/src/build/verifyAngularCoreUniqueness.d.ts +3 -1
  107. package/dist/src/cli/htmx/install.d.ts +1 -1
  108. package/dist/src/cli/scripts/bunCheck.d.ts +3 -1
  109. package/dist/src/cli/typeGraphCoherence.d.ts +9 -2
  110. package/dist/src/constants.d.ts +1 -0
  111. package/dist/src/core/appErrorHooks.d.ts +8 -0
  112. package/dist/src/core/islandPageContext.d.ts +5 -1
  113. package/dist/src/core/requestContext.d.ts +5 -0
  114. package/dist/src/core/serverOnlyStaticFiles.d.ts +7 -0
  115. package/dist/src/dev/editorTempFiles.d.ts +3 -0
  116. package/dist/src/dev/hot/analyze.d.ts +1 -1
  117. package/dist/src/dev/hot/runtime.d.ts +20 -0
  118. package/dist/src/dev/importCost/instrument.d.ts +1 -1
  119. package/dist/src/dev/rebuildTrigger.d.ts +1 -0
  120. package/dist/src/utils/cleanStaleOutputs.d.ts +4 -1
  121. package/dist/src/utils/logger.d.ts +3 -2
  122. package/dist/src/utils/resolveConvention.d.ts +1 -0
  123. package/dist/src/utils/ssrErrorPage.d.ts +3 -0
  124. package/dist/svelte/index.js +85 -18
  125. package/dist/svelte/index.js.map +11 -11
  126. package/dist/svelte/router/Route.svelte.d.ts +1 -2
  127. package/dist/svelte/router/Router.svelte +2 -4
  128. package/dist/svelte/server.js +73 -16
  129. package/dist/svelte/server.js.map +9 -9
  130. package/dist/types/globals.d.ts +3 -0
  131. package/dist/types/svelteRouter.ts +3 -12
  132. package/dist/vue/components/Image.js +2 -2
  133. package/dist/vue/components/Image.js.map +3 -3
  134. package/dist/vue/components/index.js +2 -2
  135. package/dist/vue/components/index.js.map +3 -3
  136. package/dist/vue/index.js +158 -91
  137. package/dist/vue/index.js.map +12 -12
  138. package/dist/vue/server.js +144 -88
  139. package/dist/vue/server.js.map +9 -9
  140. package/package.json +2 -1
  141. package/dist/cli/prerender-fqpp0m1j.js +0 -10
  142. /package/dist/cli/{index-vtp20gwf.js → index-2bxfhs32.js} +0 -0
  143. /package/dist/cli/{index-fgz435qn.js → index-t8jmj8w1.js} +0 -0
@@ -739,7 +739,7 @@ var init_workspacePackages = __esm(() => {
739
739
 
740
740
  // src/dev/hot/analyze.ts
741
741
  import { dirname, extname } from "path";
742
- var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scriptKindFor = (tsc, path) => {
742
+ var ANALYZER_VERSION = 2, hashText = (text) => Bun.hash(text).toString(36), scriptKindFor = (tsc, path) => {
743
743
  switch (extname(path)) {
744
744
  case ".tsx":
745
745
  return tsc.ScriptKind.TSX;
@@ -997,7 +997,23 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
997
997
  while (current && (tsc.isParenthesizedExpression(current) || tsc.isAsExpression(current) || tsc.isSatisfiesExpression(current)))
998
998
  current = current.expression;
999
999
  return current !== undefined && (tsc.isArrowFunction(current) || tsc.isFunctionExpression(current));
1000
- }, IDENTIFIER, rejection = (reason) => ({
1000
+ }, IDENTIFIER, STATEFUL_FACTORIES, STATIC_IDENTIFIERS, isStaticExpression = (tsc, node) => {
1001
+ if (tsc.isParenthesizedExpression(node) || tsc.isAsExpression(node) || tsc.isSatisfiesExpression(node) || tsc.isSpreadElement(node) || tsc.isSpreadAssignment(node))
1002
+ return isStaticExpression(tsc, node.expression);
1003
+ if (tsc.isStringLiteral(node) || tsc.isNumericLiteral(node) || tsc.isBigIntLiteral(node) || tsc.isNoSubstitutionTemplateLiteral(node) || tsc.isRegularExpressionLiteral(node) || node.kind === tsc.SyntaxKind.TrueKeyword || node.kind === tsc.SyntaxKind.FalseKeyword || node.kind === tsc.SyntaxKind.NullKeyword || tsc.isOmittedExpression(node))
1004
+ return true;
1005
+ if (tsc.isIdentifier(node))
1006
+ return STATIC_IDENTIFIERS.has(node.text);
1007
+ if (tsc.isPrefixUnaryExpression(node))
1008
+ return isStaticExpression(tsc, node.operand);
1009
+ if (tsc.isArrayLiteralExpression(node))
1010
+ return node.elements.every((element) => isStaticExpression(tsc, element));
1011
+ if (tsc.isObjectLiteralExpression(node))
1012
+ return node.properties.every((property) => tsc.isSpreadAssignment(property) ? isStaticExpression(tsc, property) : tsc.isPropertyAssignment(property) && (!tsc.isComputedPropertyName(property.name) || isStaticExpression(tsc, property.name.expression)) && isStaticExpression(tsc, property.initializer));
1013
+ if (tsc.isTemplateExpression(node))
1014
+ return node.templateSpans.every((span) => isStaticExpression(tsc, span.expression));
1015
+ return false;
1016
+ }, rejection = (reason) => ({
1001
1017
  ok: false,
1002
1018
  reason
1003
1019
  }), addImport = (scope, local, binding) => {
@@ -1008,14 +1024,23 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
1008
1024
  if (!clause || clause.isTypeOnly)
1009
1025
  return;
1010
1026
  const specifier = statement.moduleSpecifier;
1011
- const module = tsc.isStringLiteralLike(specifier) || tsc.isIdentifier(specifier) ? resolveImport(specifier.text) : undefined;
1027
+ const written = tsc.isStringLiteralLike(specifier) || tsc.isIdentifier(specifier) ? specifier.text : undefined;
1028
+ const module = written === undefined ? undefined : resolveImport(written);
1012
1029
  if (clause.name)
1013
- addImport(scope, clause.name.text, { module, name: "default" });
1030
+ addImport(scope, clause.name.text, {
1031
+ module,
1032
+ name: "default",
1033
+ specifier: written
1034
+ });
1014
1035
  const bindings = clause.namedBindings;
1015
1036
  if (!bindings)
1016
1037
  return;
1017
1038
  if (tsc.isNamespaceImport(bindings)) {
1018
- addImport(scope, bindings.name.text, { module, name: "*" });
1039
+ addImport(scope, bindings.name.text, {
1040
+ module,
1041
+ name: "*",
1042
+ specifier: written
1043
+ });
1019
1044
  return;
1020
1045
  }
1021
1046
  for (const element of bindings.elements) {
@@ -1023,7 +1048,8 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
1023
1048
  continue;
1024
1049
  addImport(scope, element.name.text, {
1025
1050
  module,
1026
- name: (element.propertyName ?? element.name).text
1051
+ name: (element.propertyName ?? element.name).text,
1052
+ specifier: written
1027
1053
  });
1028
1054
  }
1029
1055
  }, recordExportList = (tsc, scope, statement) => {
@@ -1397,8 +1423,58 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
1397
1423
  edit(node.getStart(file), node.end - node.getStart(file), replacement);
1398
1424
  }
1399
1425
  };
1426
+ const statefulCall = (reference) => {
1427
+ if (reference.deferred || !reference.call)
1428
+ return;
1429
+ const imported = imports.get(reference.name);
1430
+ const factories = imported?.specifier ? STATEFUL_FACTORIES.get(imported.specifier) : undefined;
1431
+ if (!imported || !factories)
1432
+ return;
1433
+ const viaNamespace = imported.name === "*" && reference.member !== undefined;
1434
+ const name = viaNamespace ? reference.member : imported.name;
1435
+ const callee = viaNamespace ? reference.node.parent : reference.node;
1436
+ const call = callee.parent;
1437
+ if (!name || !factories.has(name) || !call || !tsc.isCallExpression(call) || call.expression !== callee)
1438
+ return;
1439
+ return { call, name };
1440
+ };
1441
+ const ownerId = (statement, node) => {
1442
+ const { node: root } = statement;
1443
+ if (!tsc.isVariableStatement(root))
1444
+ return statement.id;
1445
+ const declaration = root.declarationList.declarations.find((item) => item.pos <= node.pos && node.end <= item.end);
1446
+ const name = declaration ? bindingNames(tsc, declaration.name)[0] : undefined;
1447
+ if (!name)
1448
+ return statement.id;
1449
+ return statement.kind === "let" ? `${statement.id}:${name}` : `v:${name}`;
1450
+ };
1451
+ const rewriteStatefulCalls = (statement) => {
1452
+ if (statement.kind === "import" || statement.kind === "skip")
1453
+ return;
1454
+ const occurrences = new Map;
1455
+ for (const reference of statement.refs) {
1456
+ const found = statefulCall(reference);
1457
+ if (!found)
1458
+ continue;
1459
+ const { call, name } = found;
1460
+ const owner = ownerId(statement, call);
1461
+ const line = file.getLineAndCharacterOfPosition(call.getStart(file)).line + 1;
1462
+ const label = quote(`${name}(\u2026) on line ${line}`);
1463
+ const isStatic = call.arguments.every((argument) => isStaticExpression(tsc, argument));
1464
+ if (!isStatic && hasTopLevelAwait(tsc, call))
1465
+ continue;
1466
+ const textHash = hashText(call.getText(file));
1467
+ const occurrence = occurrences.get(`${owner}#${textHash}`) ?? 0;
1468
+ occurrences.set(`${owner}#${textHash}`, occurrence + 1);
1469
+ const key = quote(`${owner}#${textHash}#${occurrence}`);
1470
+ edit(call.getStart(file), 0, isStatic ? `__absH.p(${quote(owner)},${key},${label},()=>(` : `__absH.w(${quote(owner)},${key},${label},()=>(`);
1471
+ edit(call.end, 0, "))");
1472
+ }
1473
+ };
1400
1474
  for (const statement of statements)
1401
1475
  rewriteStatement(statement);
1476
+ for (const statement of statements)
1477
+ rewriteStatefulCalls(statement);
1402
1478
  for (const statement of statements)
1403
1479
  rewriteCellReferences(statement);
1404
1480
  edits.sort(compareEdits);
@@ -1453,6 +1529,10 @@ var ANALYZER_VERSION = 1, hashText = (text) => Bun.hash(text).toString(36), scri
1453
1529
  };
1454
1530
  var init_analyze = __esm(() => {
1455
1531
  IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
1532
+ STATEFUL_FACTORIES = new Map([
1533
+ ["@absolutejs/scoped-state", new Set(["scopedState"])]
1534
+ ]);
1535
+ STATIC_IDENTIFIERS = new Set(["Infinity", "NaN", "undefined"]);
1456
1536
  });
1457
1537
 
1458
1538
  // src/dev/hot/runtime.ts
@@ -1464,6 +1544,7 @@ var records, recordFor = (path) => {
1464
1544
  const created = {
1465
1545
  cells: {},
1466
1546
  generation: 0,
1547
+ instances: new Map,
1467
1548
  keys: new Map,
1468
1549
  lastChanged: new Set,
1469
1550
  lastTimings: new Map,
@@ -1472,6 +1553,7 @@ var records, recordFor = (path) => {
1472
1553
  revisions: new Map,
1473
1554
  slots: new Map,
1474
1555
  tag: 0,
1556
+ warned: new Set,
1475
1557
  watchers: new Map
1476
1558
  };
1477
1559
  records.set(path, created);
@@ -1514,6 +1596,7 @@ var records, recordFor = (path) => {
1514
1596
  record.generation += 1;
1515
1597
  const pending = {
1516
1598
  generation: record.generation,
1599
+ instances: new Map,
1517
1600
  meta,
1518
1601
  reran: new Set,
1519
1602
  seen: new Set,
@@ -1549,6 +1632,7 @@ var records, recordFor = (path) => {
1549
1632
  pending.slots.set(id, { generation, value });
1550
1633
  return value;
1551
1634
  };
1635
+ const where = (label) => `${label} of ${relative(process.cwd(), path) || path}`;
1552
1636
  return {
1553
1637
  cells: record.cells,
1554
1638
  c: (name, id, init) => {
@@ -1576,6 +1660,14 @@ var records, recordFor = (path) => {
1576
1660
  return previous.value;
1577
1661
  return store(id, await ownedAsync(id, factory));
1578
1662
  },
1663
+ p: (owner, key, label, create) => {
1664
+ const previous = record.instances.get(key);
1665
+ const value = previous ? previous.value : create();
1666
+ if (!previous && record.keys.has(owner))
1667
+ console.log(`[hmr] ${where(label)} is new or changed, so its state starts empty`);
1668
+ pending.instances.set(key, { owner, value });
1669
+ return value;
1670
+ },
1579
1671
  r: (id, effect) => {
1580
1672
  if (kept(id))
1581
1673
  return;
@@ -1587,6 +1679,13 @@ var records, recordFor = (path) => {
1587
1679
  return;
1588
1680
  await ownedAsync(id, effect);
1589
1681
  store(id, undefined);
1682
+ },
1683
+ w: (owner, key, label, create) => {
1684
+ if (record.keys.has(owner) && !record.warned.has(key)) {
1685
+ record.warned.add(key);
1686
+ console.warn(`[hmr] ${where(label)} ran again because its statement re-ran, so the state it holds was reset. Create it in its own top-level const (const visitorState = scopedState({ \u2026 })) and pass that to .use() to keep it across edits.`);
1687
+ }
1688
+ return create();
1590
1689
  }
1591
1690
  };
1592
1691
  }, exportToken = (module, name, call) => {
@@ -1612,6 +1711,12 @@ var records, recordFor = (path) => {
1612
1711
  await Promise.all([...replaced.values()].map((slot) => disposeValue(slot.value).catch((error) => console.error(`[hmr] disposing a replaced value failed:`, error))));
1613
1712
  if (record.generation > 1)
1614
1713
  logDisposed(record.path, counts);
1714
+ }, keptInstances = (record, pending) => {
1715
+ const instances = new Map(pending.instances);
1716
+ for (const [key, instance] of record.instances)
1717
+ if (!instances.has(key) && pending.seen.has(instance.owner) && !pending.reran.has(instance.owner))
1718
+ instances.set(key, instance);
1719
+ return instances;
1615
1720
  }, commit = (record, pending) => {
1616
1721
  if (record.pending !== pending)
1617
1722
  return;
@@ -1625,6 +1730,7 @@ var records, recordFor = (path) => {
1625
1730
  record.keys = pending.keys;
1626
1731
  record.lastChanged = pending.changedExports;
1627
1732
  record.lastTimings = pending.timings;
1733
+ record.instances = keptInstances(record, pending);
1628
1734
  for (const name of pending.changedExports)
1629
1735
  record.revisions.set(name, (record.revisions.get(name) ?? 0) + 1);
1630
1736
  if (replaced.size === 0)