@happyvertical/smrt-cli 0.55.17 → 0.55.19

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 (28) hide show
  1. package/AGENTS.md +4 -0
  2. package/dist/{AuditLogCollection-WDHL-_AF-02D9mDot.js → AuditLogCollection-WDHL-_AF-CnfYFIBN.js} +1 -1
  3. package/dist/{OidcIdentity-BUblgQLs-hzB0lrxA.js → OidcIdentity-BUblgQLs-7b_UC2ND.js} +2 -2
  4. package/dist/{OidcIdentityCollection-ClOyaHeF-D7aAvcYW.js → OidcIdentityCollection-ClOyaHeF-1rB63uyR.js} +1 -1
  5. package/dist/{ProfileAssetCollection-CRMcjLJ--CRQsOHTZ.js → ProfileAssetCollection-CRMcjLJ--DCdP8aB2.js} +3 -3
  6. package/dist/{ProfileCollection-C4INKh7Y-DUcvwlG4.js → ProfileCollection-C4INKh7Y-j9kXNQ27.js} +17 -17
  7. package/dist/{ProfileLinkCollection-DFfZRsgv-4ulMzaJO.js → ProfileLinkCollection-DFfZRsgv-BsJWD-cT.js} +2 -2
  8. package/dist/{ProfileMetadataCollection-BIYKy4Y3-BGVdmb-k.js → ProfileMetadataCollection-BIYKy4Y3-StzZmRNZ.js} +1 -1
  9. package/dist/{ProfileMetafieldCollection-Buz7gzag-Cuuzb-Yt.js → ProfileMetafieldCollection-Buz7gzag-ByG3A0cx.js} +1 -1
  10. package/dist/{ProfileRelationshipCollection-C4g_QLI4-DHnLSOex.js → ProfileRelationshipCollection-C4g_QLI4-JgyUD2Y3.js} +1 -1
  11. package/dist/{ProfileTypeCollection-B7kwi1mU-tffgStmE.js → ProfileTypeCollection-B7kwi1mU-C-XBj2lo.js} +2 -2
  12. package/dist/app/portability.d.ts +23 -1
  13. package/dist/app/portability.d.ts.map +1 -1
  14. package/dist/app.js +2 -2
  15. package/dist/{cli-FDpY0gCt.js → cli-Cgj-996T.js} +1 -1
  16. package/dist/commands/utilities.d.ts.map +1 -1
  17. package/dist/{commands-Cve0qfxc.js → commands-DECEmNZO.js} +321 -3
  18. package/dist/dist-CBcZA5mL.js +21 -0
  19. package/dist/{dist-BtlfVNPV.js → dist-CEfXh68v.js} +4 -4
  20. package/dist/{dist-BQ5ZrH9U.js → dist-CGo6iLhL.js} +444 -57
  21. package/dist/{dist-BQjejZ0-.js → dist-CO2CxT1e.js} +3 -3
  22. package/dist/{dist-C_pNF2sM.js → dist-C_8Edimn.js} +2 -2
  23. package/dist/index.js +12 -12
  24. package/dist/{oidcProvisioningPrimitives-DNP9EZlU-B5DmitVf.js → oidcProvisioningPrimitives-DNP9EZlU-ZOiVGqhG.js} +3 -3
  25. package/dist/{portability-BbltyqQ-.js → portability-ClYZy8ut.js} +28 -5
  26. package/dist/{resolveIdentity-CkyqvctR-BDafA2yA.js → resolveIdentity-CkyqvctR-CQD6ChY1.js} +14 -14
  27. package/package.json +16 -16
  28. package/dist/dist-DHRli5Sc.js +0 -21
@@ -7763,6 +7763,304 @@ var playgroundCommands = {
7763
7763
  }
7764
7764
  };
7765
7765
  //#endregion
7766
+ //#region src/commands/cascade-layers-reset-scan.mjs
7767
+ /**
7768
+ * Unlayered universal-reset scanner for `smrt doctor` (#3862).
7769
+ *
7770
+ * Unlayered CSS beats every `@layer` regardless of specificity. A host's
7771
+ * unlayered `*, *::before, *::after { margin: 0; padding: 0 }` (or Tailwind
7772
+ * preflight / normalize.css / sanitize.css imported without a layer) therefore
7773
+ * strips padding from every smrt component, whose rules sit in `smrt.theme`.
7774
+ *
7775
+ * Dependency-free ESM so the monorepo lint gate
7776
+ * (`scripts/check-smrt-cascade-layers.mjs`) can run the same code under plain
7777
+ * `node`. Parsing is deliberately small: comments and string literals are
7778
+ * skipped, `@layer` blocks are tracked by brace depth, and only style rules
7779
+ * and `@import` statements outside any layer are reported.
7780
+ */
7781
+ var SMRT_PACKAGES = ["@happyvertical/smrt-ui", "@happyvertical/smrt-svelte"];
7782
+ var RESET_RECIPE = "wrap the reset in @layer smrt.reset { … }, or import it with @import url(...) layer(smrt.reset); see packages/smrt-ui/agents/styling.md#cascade-layers";
7783
+ var UNIVERSAL_SELECTOR = /^(\*(::(before|after|backdrop|marker|placeholder))?|html|body)$/;
7784
+ var RESET_DECLARATION = /^(margin|padding)(?:-[a-z-]+)?\s*:\s*0(?:px|rem|em|%)?(?=[\s!;]|$)/i;
7785
+ var RESET_IMPORT_NAME = /normalize|sanitize|reset|preflight/i;
7786
+ /**
7787
+ * Replace comments with spaces (newlines kept so line numbers stay true),
7788
+ * leaving string literals such as `content: "/* x *\/"` untouched.
7789
+ *
7790
+ * @param {string} css
7791
+ * @returns {string}
7792
+ */
7793
+ function stripCssComments(css) {
7794
+ let out = "";
7795
+ let quote = null;
7796
+ for (let i = 0; i < css.length; i += 1) {
7797
+ const c = css[i];
7798
+ if (quote) {
7799
+ out += c;
7800
+ if (c === "\\" && i + 1 < css.length) {
7801
+ out += css[i + 1];
7802
+ i += 1;
7803
+ } else if (c === quote) quote = null;
7804
+ continue;
7805
+ }
7806
+ if (c === "\"" || c === "'") {
7807
+ quote = c;
7808
+ out += c;
7809
+ continue;
7810
+ }
7811
+ if (c === "/" && css[i + 1] === "*") {
7812
+ const end = css.indexOf("*/", i + 2);
7813
+ const stop = end === -1 ? css.length : end + 2;
7814
+ out += css.slice(i, stop).replace(/[^\n]/g, " ");
7815
+ i = stop - 1;
7816
+ continue;
7817
+ }
7818
+ out += c;
7819
+ }
7820
+ return out;
7821
+ }
7822
+ /**
7823
+ * 1-based line lookup for `source`: newline offsets are indexed once, then each
7824
+ * lookup is a binary search, so large minified stylesheets stay linear-ish.
7825
+ *
7826
+ * @param {string} source
7827
+ * @returns {(index: number) => number}
7828
+ */
7829
+ function lineIndex(source) {
7830
+ const starts = [0];
7831
+ for (let i = 0; i < source.length; i += 1) if (source[i] === "\n") starts.push(i + 1);
7832
+ return (index) => {
7833
+ let lo = 0;
7834
+ let hi = starts.length - 1;
7835
+ while (lo < hi) {
7836
+ const mid = lo + hi + 1 >> 1;
7837
+ if (starts[mid] <= index) lo = mid;
7838
+ else hi = mid - 1;
7839
+ }
7840
+ return lo + 1;
7841
+ };
7842
+ }
7843
+ /**
7844
+ * Svelte scoping: `:global(X)` is the unscoped selector X, and `:global { … }`
7845
+ * is a block of unscoped rules. Both are rewritten so the global form is
7846
+ * matched. Newlines are kept, so line numbers do not move.
7847
+ *
7848
+ * @param {string} body
7849
+ * @returns {string}
7850
+ */
7851
+ function unwrapSvelteGlobal(body) {
7852
+ const token = ":global(";
7853
+ let out = "";
7854
+ let cursor = 0;
7855
+ for (;;) {
7856
+ const start = body.indexOf(token, cursor);
7857
+ if (start === -1) break;
7858
+ out += body.slice(cursor, start);
7859
+ let depth = 1;
7860
+ let end = start + 8;
7861
+ while (end < body.length && depth > 0) {
7862
+ if (body[end] === "(") depth += 1;
7863
+ else if (body[end] === ")") depth -= 1;
7864
+ end += 1;
7865
+ }
7866
+ if (depth > 0) {
7867
+ out += body.slice(start);
7868
+ return out.replace(/:global\s*\{/g, "@global {");
7869
+ }
7870
+ out += body.slice(start + 8, end - 1);
7871
+ cursor = end;
7872
+ }
7873
+ out += body.slice(cursor);
7874
+ return out.replace(/:global\s*\{/g, "@global {");
7875
+ }
7876
+ /**
7877
+ * Find unlayered universal resets and unlayered reset `@import`s in a
7878
+ * stylesheet. Rules inside any `@layer` block (including `@layer smrt.*` and
7879
+ * Tailwind's `@layer base`) are never reported.
7880
+ *
7881
+ * @param {string} css
7882
+ * @returns {Array<{ line: number, kind: 'rule' | 'import', selector: string, detail: string }>}
7883
+ */
7884
+ function findUnlayeredResets(css) {
7885
+ const src = stripCssComments(css);
7886
+ const lineOf = lineIndex(src);
7887
+ const findings = [];
7888
+ /** @type {Array<{ kind: 'layer' | 'rule' | 'group', head: string, headLine: number, decls: string[] }>} */
7889
+ const stack = [];
7890
+ let prelude = "";
7891
+ let preludeStart = -1;
7892
+ let quote = null;
7893
+ const flushDecl = () => {
7894
+ const text = prelude.trim();
7895
+ const frame = stack[stack.length - 1];
7896
+ if (text && frame && frame.kind === "rule") frame.decls.push(text);
7897
+ prelude = "";
7898
+ preludeStart = -1;
7899
+ };
7900
+ for (let i = 0; i < src.length; i += 1) {
7901
+ const c = src[i];
7902
+ if (quote) {
7903
+ prelude += c;
7904
+ if (c === "\\" && i + 1 < src.length) {
7905
+ prelude += src[i + 1];
7906
+ i += 1;
7907
+ } else if (c === quote) quote = null;
7908
+ continue;
7909
+ }
7910
+ if (c === "\"" || c === "'") {
7911
+ quote = c;
7912
+ if (preludeStart === -1) preludeStart = i;
7913
+ prelude += c;
7914
+ continue;
7915
+ }
7916
+ if (c === "{") {
7917
+ const head = prelude.trim();
7918
+ const headIndex = preludeStart === -1 ? i : preludeStart;
7919
+ let kind = "rule";
7920
+ if (/^@layer\b/i.test(head)) kind = "layer";
7921
+ else if (head.startsWith("@")) kind = "group";
7922
+ stack.push({
7923
+ kind,
7924
+ head,
7925
+ headLine: lineOf(headIndex),
7926
+ decls: []
7927
+ });
7928
+ prelude = "";
7929
+ preludeStart = -1;
7930
+ continue;
7931
+ }
7932
+ if (c === "}") {
7933
+ flushDecl();
7934
+ const frame = stack.pop();
7935
+ if (frame && frame.kind === "rule" && !stack.some((f) => f.kind === "layer")) {
7936
+ const universal = frame.head.split(",").map((selector) => selector.trim()).filter((selector) => UNIVERSAL_SELECTOR.test(selector));
7937
+ const resetDecls = frame.decls.filter((decl) => RESET_DECLARATION.test(decl));
7938
+ if (universal.length > 0 && resetDecls.length > 0) findings.push({
7939
+ line: frame.headLine,
7940
+ kind: "rule",
7941
+ selector: frame.head,
7942
+ detail: resetDecls.join("; ")
7943
+ });
7944
+ }
7945
+ continue;
7946
+ }
7947
+ if (c === ";") {
7948
+ const head = prelude.trim();
7949
+ if (stack.length === 0 && /^@import\b/i.test(head)) {
7950
+ const url = head.match(/@import\s+(?:url\(\s*)?["']?([^"')\s;]+)/i);
7951
+ const rest = head.replace(/url\([^)]*\)|"[^"]*"|'[^']*'/gi, " ");
7952
+ if (!/\blayer\b/i.test(rest) && url && RESET_IMPORT_NAME.test(url[1])) findings.push({
7953
+ line: lineOf(preludeStart === -1 ? i : preludeStart),
7954
+ kind: "import",
7955
+ selector: head,
7956
+ detail: `unlayered import of ${url[1]}`
7957
+ });
7958
+ }
7959
+ flushDecl();
7960
+ continue;
7961
+ }
7962
+ if (preludeStart === -1 && !/\s/.test(c)) preludeStart = i;
7963
+ prelude += c;
7964
+ }
7965
+ return findings;
7966
+ }
7967
+ /**
7968
+ * Svelte `<style>` bodies, with findings mapped to the `.svelte` file's lines.
7969
+ *
7970
+ * @param {string} source
7971
+ * @returns {Array<{ line: number, kind: 'rule' | 'import', selector: string, detail: string }>}
7972
+ */
7973
+ function findUnlayeredResetsInSvelte(source) {
7974
+ const findings = [];
7975
+ const lineOf = lineIndex(source);
7976
+ const pattern = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
7977
+ let match;
7978
+ while ((match = pattern.exec(source)) !== null) {
7979
+ const bodyStart = match.index + match[0].indexOf(">") + 1;
7980
+ for (const finding of findUnlayeredResets(unwrapSvelteGlobal(match[1]))) findings.push({
7981
+ ...finding,
7982
+ line: lineOf(bodyStart) + finding.line - 1
7983
+ });
7984
+ }
7985
+ return findings;
7986
+ }
7987
+ var SKIP_DIRS = /* @__PURE__ */ new Set([
7988
+ "node_modules",
7989
+ "dist",
7990
+ ".svelte-kit",
7991
+ ".git",
7992
+ "build",
7993
+ ".turbo"
7994
+ ]);
7995
+ /**
7996
+ * Project CSS sources: `src/**` `*.css` and `*.svelte`, plus a root `app.css`.
7997
+ *
7998
+ * @param {string} root
7999
+ * @returns {string[]} absolute paths
8000
+ */
8001
+ function listCascadeSources(root) {
8002
+ const files = [];
8003
+ const walk = (dir) => {
8004
+ for (const entry of readdirSync(dir, { withFileTypes: true })) if (entry.isDirectory()) {
8005
+ if (!SKIP_DIRS.has(entry.name)) walk(join(dir, entry.name));
8006
+ } else if (entry.isFile() && /\.(css|svelte)$/.test(entry.name)) files.push(join(dir, entry.name));
8007
+ };
8008
+ const src = join(root, "src");
8009
+ if (existsSync(src) && statSync(src).isDirectory()) walk(src);
8010
+ const rootCss = join(root, "app.css");
8011
+ if (existsSync(rootCss) && !files.includes(rootCss)) files.push(rootCss);
8012
+ return files;
8013
+ }
8014
+ /**
8015
+ * Whether a project's package.json depends on smrt-ui or smrt-svelte.
8016
+ *
8017
+ * @param {Record<string, any> | null | undefined} packageJson
8018
+ * @returns {boolean}
8019
+ */
8020
+ function dependsOnSmrtUi(packageJson) {
8021
+ return [
8022
+ "dependencies",
8023
+ "devDependencies",
8024
+ "peerDependencies"
8025
+ ].some((section) => SMRT_PACKAGES.some((name) => Boolean(packageJson?.[section]?.[name])));
8026
+ }
8027
+ /**
8028
+ * Scan a project root for unlayered universal resets.
8029
+ *
8030
+ * @param {string} root project directory
8031
+ * @param {{ packageJson?: Record<string, any> | null, requireSmrtDependency?: boolean }} [options]
8032
+ * `requireSmrtDependency` (default true) skips projects that do not depend on
8033
+ * smrt-ui/smrt-svelte. Monorepo passes set it to false.
8034
+ * @returns {{
8035
+ * applies: boolean,
8036
+ * filesScanned: number,
8037
+ * findings: Array<{ file: string, line: number, kind: 'rule' | 'import', selector: string, detail: string }>,
8038
+ * }}
8039
+ */
8040
+ function scanProjectForUnlayeredResets(root, options = {}) {
8041
+ const applies = !(options.requireSmrtDependency !== false) || dependsOnSmrtUi(options.packageJson);
8042
+ if (!applies) return {
8043
+ applies,
8044
+ filesScanned: 0,
8045
+ findings: []
8046
+ };
8047
+ const files = listCascadeSources(root);
8048
+ const findings = [];
8049
+ for (const file of files) {
8050
+ const text = readFileSync(file, "utf8");
8051
+ const found = file.endsWith(".svelte") ? findUnlayeredResetsInSvelte(text) : findUnlayeredResets(text);
8052
+ for (const finding of found) findings.push({
8053
+ file: relative(root, file),
8054
+ ...finding
8055
+ });
8056
+ }
8057
+ return {
8058
+ applies,
8059
+ filesScanned: files.length,
8060
+ findings
8061
+ };
8062
+ }
8063
+ //#endregion
7766
8064
  //#region src/commands/db-drop-framework-base-tables.ts
7767
8065
  /**
7768
8066
  * db:drop-framework-base-tables Command
@@ -7914,7 +8212,7 @@ var dbMaterializeTenantHierarchyCommand = {
7914
8212
  if (!config.database?.url || config.database.url === ":memory:") throw new Error("Database configuration required for db:materialize-tenant-hierarchy. Configure database.url in smrt.config.ts (or DATABASE_URL).");
7915
8213
  const dbType = config.database.type || "sqlite";
7916
8214
  const { getDatabase } = await import("@happyvertical/sql");
7917
- const { materializeTenantHierarchy, TenantHierarchyMaterializationError } = await import("./dist-BQ5ZrH9U.js");
8215
+ const { materializeTenantHierarchy, TenantHierarchyMaterializationError } = await import("./dist-CGo6iLhL.js");
7918
8216
  db = await getDatabase({
7919
8217
  type: dbType,
7920
8218
  url: config.database.url
@@ -8123,7 +8421,7 @@ var dbMigrateLedgerAccountsCommand = {
8123
8421
  if (dbType !== "postgres") throw new Error("db:migrate-ledger-accounts currently supports PostgreSQL only; no changes were made. See the smrt-ledgers README for SQLite.");
8124
8422
  await autoDiscoverAndLoad();
8125
8423
  const { getDatabase } = await import("@happyvertical/sql");
8126
- const { migrateLedgerAccountsTable, planLedgerAccountsTableMove } = await import("./dist-BtlfVNPV.js");
8424
+ const { migrateLedgerAccountsTable, planLedgerAccountsTableMove } = await import("./dist-CEfXh68v.js");
8127
8425
  db = await getDatabase({
8128
8426
  type: dbType,
8129
8427
  url: config.database.url
@@ -10523,7 +10821,7 @@ var utilityCommands = {
10523
10821
  description: "Application operations: install, setup, recover, start, stop, doctor, open, backup, export, import, migrate, worker, dev, build (`smrt app help`)",
10524
10822
  args: ["[operation]", "[args...]"],
10525
10823
  handler: async (args) => {
10526
- const { runAppCommand } = await import("./cli-FDpY0gCt.js").then((n) => n.r);
10824
+ const { runAppCommand } = await import("./cli-Cgj-996T.js").then((n) => n.r);
10527
10825
  const code = await runAppCommand(args);
10528
10826
  if (code !== 0) process.exitCode = code;
10529
10827
  }
@@ -12014,6 +12312,26 @@ export default testManifest;
12014
12312
  else if (existsSync(envExamplePath)) check(".env file exists", false, void 0, ".env.example exists - copy it to .env");
12015
12313
  else check(".env file exists", false, void 0, "No .env file - environment variables may be needed");
12016
12314
  console.log();
12315
+ let cascadeLayers = {
12316
+ applies: false,
12317
+ filesScanned: 0,
12318
+ findings: []
12319
+ };
12320
+ try {
12321
+ cascadeLayers = scanProjectForUnlayeredResets(cwd, { packageJson });
12322
+ } catch (error) {
12323
+ warnings.push(`cascade-layer scan skipped: ${error instanceof Error ? error.message : String(error)}`);
12324
+ }
12325
+ if (cascadeLayers.applies) {
12326
+ console.log("🎨 Cascade Layers\n");
12327
+ if (cascadeLayers.findings.length === 0) passed.push("No unlayered universal resets");
12328
+ else for (const finding of cascadeLayers.findings) {
12329
+ const message = `unlayered reset at ${finding.file}:${finding.line} (${finding.selector}): ${RESET_RECIPE}`;
12330
+ warnings.push(message);
12331
+ console.log(` ⚠️ ${message}`);
12332
+ }
12333
+ console.log();
12334
+ }
12017
12335
  if (options.db) {
12018
12336
  console.log("🗄️ Live Database Parity\n");
12019
12337
  const { report, error, database } = await runLiveSchemaParity({ discover: false });
@@ -0,0 +1,21 @@
1
+ import "./NostrIdentity-AC8fAdxe-DKxKyu_a.js";
2
+ import "./ApiKey-CVmlf3V6-BAeHuH48.js";
3
+ import "./ApiKeyCollection-CiiDNFdD-CMGF3_ul.js";
4
+ import "./AuditLogCollection-WDHL-_AF-CnfYFIBN.js";
5
+ import "./NostrIdentityCollection-CctlECpX-fkAPnK02.js";
6
+ import "./OidcIdentity-BUblgQLs-7b_UC2ND.js";
7
+ import "./OidcIdentityCollection-ClOyaHeF-1rB63uyR.js";
8
+ import "./ProfileAssetCollection-CRMcjLJ--DCdP8aB2.js";
9
+ import { a as ProfileCollection } from "./ProfileCollection-C4INKh7Y-j9kXNQ27.js";
10
+ import "./ProfileLinkCollection-DFfZRsgv-BsJWD-cT.js";
11
+ import "./ProfileMetadataCollection-BIYKy4Y3-StzZmRNZ.js";
12
+ import "./ProfileMetafieldCollection-Buz7gzag-ByG3A0cx.js";
13
+ import "./ProfileRelationshipCollection-C4g_QLI4-JgyUD2Y3.js";
14
+ import "./ProfileRelationshipTermCollection-C9J7mC7P-ChIFqjCg.js";
15
+ import "./ProfileRelationshipType-U7IwF6Wf-DNavf24e.js";
16
+ import "./ProfileRelationshipTypeCollection-_n22WqTb-DiZUWvev.js";
17
+ import "./ProfileTypeCollection-B7kwi1mU-C-XBj2lo.js";
18
+ import "./oidcProvisioningPrimitives-DNP9EZlU-ZOiVGqhG.js";
19
+ import "./resolveIdentity-CkyqvctR-CQD6ChY1.js";
20
+ import "./dist-CGo6iLhL.js";
21
+ export { ProfileCollection };