@sprig-and-prose/sprig-universe 0.1.0

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 (45) hide show
  1. package/PHILOSOPHY.md +201 -0
  2. package/README.md +168 -0
  3. package/REFERENCE.md +355 -0
  4. package/biome.json +24 -0
  5. package/package.json +30 -0
  6. package/repositories/sprig-repository-github/index.js +29 -0
  7. package/src/ast.js +257 -0
  8. package/src/cli.js +1510 -0
  9. package/src/graph.js +950 -0
  10. package/src/index.js +46 -0
  11. package/src/ir.js +121 -0
  12. package/src/parser.js +1656 -0
  13. package/src/scanner.js +255 -0
  14. package/src/scene-manifest.js +856 -0
  15. package/src/util/span.js +46 -0
  16. package/src/util/text.js +126 -0
  17. package/src/validator.js +862 -0
  18. package/src/validators/mysql/connection.js +154 -0
  19. package/src/validators/mysql/schema.js +209 -0
  20. package/src/validators/mysql/type-compat.js +219 -0
  21. package/src/validators/mysql/validator.js +332 -0
  22. package/test/fixtures/amaranthine-mini.prose +53 -0
  23. package/test/fixtures/conflicting-universes-a.prose +8 -0
  24. package/test/fixtures/conflicting-universes-b.prose +8 -0
  25. package/test/fixtures/duplicate-names.prose +20 -0
  26. package/test/fixtures/first-line-aware.prose +32 -0
  27. package/test/fixtures/indented-describe.prose +18 -0
  28. package/test/fixtures/multi-file-universe-a.prose +15 -0
  29. package/test/fixtures/multi-file-universe-b.prose +15 -0
  30. package/test/fixtures/multi-file-universe-conflict-desc.prose +12 -0
  31. package/test/fixtures/multi-file-universe-conflict-title.prose +4 -0
  32. package/test/fixtures/multi-file-universe-with-title.prose +10 -0
  33. package/test/fixtures/named-document.prose +17 -0
  34. package/test/fixtures/named-duplicate.prose +22 -0
  35. package/test/fixtures/named-reference.prose +17 -0
  36. package/test/fixtures/relates-errors.prose +38 -0
  37. package/test/fixtures/relates-tier1.prose +14 -0
  38. package/test/fixtures/relates-tier2.prose +16 -0
  39. package/test/fixtures/relates-tier3.prose +21 -0
  40. package/test/fixtures/sprig-meta-mini.prose +62 -0
  41. package/test/fixtures/unresolved-relates.prose +15 -0
  42. package/test/fixtures/using-in-references.prose +35 -0
  43. package/test/fixtures/using-unknown.prose +8 -0
  44. package/test/universe-basic.test.js +804 -0
  45. package/tsconfig.json +15 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @fileoverview Source span utilities for tracking source locations
3
+ */
4
+
5
+ /**
6
+ * @typedef {Object} SourceSpan
7
+ * @property {string} file - File path
8
+ * @property {{ line: number, col: number, offset: number }} start - Start position
9
+ * @property {{ line: number, col: number, offset: number }} end - End position
10
+ */
11
+
12
+ /**
13
+ * Creates a source span from start and end positions
14
+ * @param {string} file - File path
15
+ * @param {{ line: number, col: number, offset: number }} start - Start position
16
+ * @param {{ line: number, col: number, offset: number }} end - End position
17
+ * @returns {SourceSpan}
18
+ */
19
+ export function createSpan(file, start, end) {
20
+ return { file, start, end };
21
+ }
22
+
23
+ /**
24
+ * Creates a zero-length span at a position
25
+ * @param {string} file - File path
26
+ * @param {{ line: number, col: number, offset: number }} pos - Position
27
+ * @returns {SourceSpan}
28
+ */
29
+ export function createPointSpan(file, pos) {
30
+ return { file, start: pos, end: pos };
31
+ }
32
+
33
+ /**
34
+ * Merges two spans (from start of first to end of second)
35
+ * @param {SourceSpan} span1 - First span
36
+ * @param {SourceSpan} span2 - Second span
37
+ * @returns {SourceSpan}
38
+ */
39
+ export function mergeSpans(span1, span2) {
40
+ return {
41
+ file: span1.file,
42
+ start: span1.start,
43
+ end: span2.end,
44
+ };
45
+ }
46
+
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @fileoverview Text normalization utilities
3
+ */
4
+
5
+ /**
6
+ * Counts leading whitespace, treating tabs as 2 spaces.
7
+ * @param {string} line - Line to count indentation for
8
+ * @returns {number} - Indentation count (spaces + tabs*2)
9
+ */
10
+ function countIndent(line) {
11
+ let count = 0;
12
+ for (let i = 0; i < line.length; i++) {
13
+ if (line[i] === ' ') {
14
+ count++;
15
+ } else if (line[i] === '\t') {
16
+ count += 2; // Treat tabs as 2 spaces
17
+ } else {
18
+ break;
19
+ }
20
+ }
21
+ return count;
22
+ }
23
+
24
+ /**
25
+ * Removes exactly `amount` worth of leading whitespace (spaces/tabs).
26
+ * Treats tabs as 2 spaces when removing.
27
+ * @param {string} line - Line to remove indentation from
28
+ * @param {number} amount - Amount of indentation to remove (in space units)
29
+ * @returns {string} - Line with indentation removed
30
+ */
31
+ function removeIndent(line, amount) {
32
+ let removed = 0;
33
+ let i = 0;
34
+ while (i < line.length && removed < amount) {
35
+ if (line[i] === ' ') {
36
+ removed++;
37
+ i++;
38
+ } else if (line[i] === '\t') {
39
+ removed += 2; // Treat tabs as 2 spaces
40
+ i++;
41
+ // If we overshoot, we've removed the tab, which is fine
42
+ } else {
43
+ break;
44
+ }
45
+ }
46
+ return line.slice(i);
47
+ }
48
+
49
+ /**
50
+ * Normalizes prose blocks by removing common indentation from lines after the first non-empty line.
51
+ * This handles the common case where the first line is flush-left (indent 0) and subsequent lines
52
+ * are indented due to code formatting.
53
+ *
54
+ * Algorithm:
55
+ * 1. Split raw by '\n' into lines
56
+ * 2. Find the first non-empty line index i0
57
+ * 3. Compute minIndentAfter from lines AFTER i0 (only non-empty lines)
58
+ * 4. Keep lines[0..i0] unchanged, then for lines after i0, remove minIndentAfter worth of indentation
59
+ *
60
+ * @param {string} raw - Raw text to normalize
61
+ * @returns {string} - Normalized text
62
+ */
63
+ export function normalizeProseBlock(raw) {
64
+ const lines = raw.split('\n');
65
+
66
+ // Find first non-empty line index
67
+ let i0 = -1;
68
+ for (let i = 0; i < lines.length; i++) {
69
+ if (lines[i].trim().length > 0) {
70
+ i0 = i;
71
+ break;
72
+ }
73
+ }
74
+
75
+ // If no non-empty line found, return raw unchanged
76
+ if (i0 === -1) {
77
+ return raw;
78
+ }
79
+
80
+ // Compute minIndentAfter from lines AFTER i0 (only non-empty lines)
81
+ // Use the minimum indent to preserve relative indentation while removing common prefix
82
+ let minIndentAfter = null;
83
+ for (let i = i0 + 1; i < lines.length; i++) {
84
+ if (lines[i].trim().length > 0) {
85
+ const indent = countIndent(lines[i]);
86
+ if (minIndentAfter === null || indent < minIndentAfter) {
87
+ minIndentAfter = indent;
88
+ }
89
+ }
90
+ }
91
+
92
+ // If no non-empty lines after i0, return raw unchanged
93
+ if (minIndentAfter === null || minIndentAfter === 0) {
94
+ return raw;
95
+ }
96
+
97
+ // Produce output: keep lines[0..i0] unchanged, then remove minIndentAfter from subsequent lines
98
+ const result = [];
99
+ for (let i = 0; i < lines.length; i++) {
100
+ if (i <= i0) {
101
+ // Keep lines up to and including first non-empty line unchanged
102
+ result.push(lines[i]);
103
+ } else {
104
+ // For lines after i0
105
+ if (lines[i].trim().length === 0) {
106
+ // Empty/whitespace-only lines: keep as empty string
107
+ result.push('');
108
+ } else {
109
+ // Remove exactly minIndentAfter worth of indentation
110
+ result.push(removeIndent(lines[i], minIndentAfter));
111
+ }
112
+ }
113
+ }
114
+
115
+ return result.join('\n');
116
+ }
117
+
118
+ /**
119
+ * @deprecated Use normalizeProseBlock instead
120
+ * @param {string} raw - Raw text to dedent
121
+ * @returns {string} - Dedented text
122
+ */
123
+ export function dedentPreserve(raw) {
124
+ return normalizeProseBlock(raw);
125
+ }
126
+