Skip to content

Commit fe1b09b

Browse files
committed
Merge remote-tracking branch 'upstream/common-v8' into dev
2 parents e794858 + 46fd0ea commit fe1b09b

18 files changed

Lines changed: 592 additions & 255 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
"@evolu/common": major
3+
---
4+
5+
Refactored the Array module with breaking changes, better naming, and new helpers.
6+
7+
### Breaking Changes
8+
9+
**Removed `isNonEmptyReadonlyArray`** — use `isNonEmptyArray` instead. The function now handles both mutable and readonly arrays via overloads:
10+
11+
```ts
12+
// Before
13+
if (isNonEmptyReadonlyArray(readonlyArr)) { ... }
14+
if (isNonEmptyArray(mutableArr)) { ... }
15+
16+
// After — one function for both
17+
if (isNonEmptyArray(readonlyArr)) { ... }
18+
if (isNonEmptyArray(mutableArr)) { ... }
19+
```
20+
21+
**Renamed mutation functions** for consistency with the `...Array` suffix pattern:
22+
23+
- `shiftArray``shiftFromArray`
24+
- `popArray``popFromArray`
25+
26+
### New Functions
27+
28+
- **`flatMapArray`** — maps each element to an array and flattens the result, preserving non-empty type when applicable
29+
- **`concatArrays`** — concatenates two arrays, returning non-empty when at least one input is non-empty
30+
- **`sortArray`** — returns a new sorted array (wraps `toSorted`), preserving non-empty type
31+
- **`reverseArray`** — returns a new reversed array (wraps `toReversed`), preserving non-empty type
32+
- **`spliceArray`** — returns a new array with elements removed/replaced (wraps `toSpliced`)
33+
34+
### Migration
35+
36+
```ts
37+
// isNonEmptyReadonlyArray → isNonEmptyArray
38+
-import { isNonEmptyReadonlyArray } from "@evolu/common";
39+
+import { isNonEmptyArray } from "@evolu/common";
40+
41+
// shiftArray → shiftFromArray
42+
-import { shiftArray } from "@evolu/common";
43+
+import { shiftFromArray } from "@evolu/common";
44+
45+
// popArray → popFromArray
46+
-import { popArray } from "@evolu/common";
47+
+import { popFromArray } from "@evolu/common";
48+
```

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export type MessageType = (typeof MessageType)[keyof typeof MessageType];
120120
- **Use `### Example` instead of `@example`** - for better markdown rendering and consistency
121121
- **Write clear descriptions** - explain what the function does, not how to use it
122122
- **Use `{@link}` for references** - link to types, interfaces, functions, and exported symbols on first mention for discoverability
123+
- **Avoid pipe characters in first sentence** - TypeDoc extracts the first sentence for table descriptions, and pipe characters (even in inline code like `T | undefined`) break markdown table rendering. Move such details to subsequent sentences.
123124

124125
````ts
125126
// ✅ Good

apps/web/scripts/fix-api-reference.mts

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const reference = path.join(
88
"src/app/(docs)/docs/api-reference",
99
);
1010

11-
function rearrangeMdxFilesRecursively(dir: string) {
11+
const rearrangeMdxFilesRecursively = (dir: string): void => {
1212
for (const item of fs.readdirSync(dir)) {
1313
const fullPath = path.join(dir, item);
1414
const stat = fs.statSync(fullPath);
@@ -20,7 +20,7 @@ function rearrangeMdxFilesRecursively(dir: string) {
2020
const newFolder = path.join(dir, baseName);
2121
fs.mkdirSync(newFolder, { recursive: true });
2222
fs.renameSync(fullPath, path.join(newFolder, "page.mdx"));
23-
fixLinksInMdxFile(
23+
fixMdxFile(
2424
path.join(newFolder, "page.mdx"),
2525
`${baseName} - API reference`,
2626
);
@@ -29,13 +29,13 @@ function rearrangeMdxFilesRecursively(dir: string) {
2929
dir === reference
3030
? "API reference"
3131
: `${path.basename(dir)} - API reference`;
32-
fixLinksInMdxFile(fullPath, title);
32+
fixMdxFile(fullPath, title);
3333
}
3434
}
3535
}
36-
}
36+
};
3737

38-
function fixLinksInMdxFile(filePath: string, title: string) {
38+
const fixMdxFile = (filePath: string, title: string): void => {
3939
const content = fs.readFileSync(filePath, "utf-8");
4040
// first let's replace /page.mdx with /
4141
let newContent = content.replace(/\/page\.mdx/g, "");
@@ -56,52 +56,16 @@ function fixLinksInMdxFile(filePath: string, title: string) {
5656
},
5757
);
5858

59-
// Extract ## headings to generate sections for "On this page" navigation
60-
const sections = extractSections(newContent);
61-
const sectionsExport =
62-
sections.length > 0
63-
? `export const sections = ${JSON.stringify(sections)};`
64-
: "export const sections = [];";
59+
newContent = newContent
60+
.replace(/^export const metadata = \{ title: [^}]*\};\s*\r?\n\s*/, "")
61+
.replace(/^export const sections = .*;\s*\r?\n\s*/m, "");
6562

66-
// add meta tags (idempotent)
67-
newContent = newContent.replace(
68-
/^export const metadata = \{ title: [^}]*\};\s*\r?\n\s*/,
69-
"",
70-
);
71-
newContent = newContent.replace(
72-
/^export const sections = .*;\s*\r?\n\s*/m,
73-
"",
74-
);
7563
newContent = `export const metadata = { title: '${title}' };
76-
${sectionsExport}
7764
7865
${newContent}`;
7966

8067
fs.writeFileSync(filePath, newContent);
81-
}
82-
83-
/** Extract ## headings from MDX content to generate sections */
84-
function extractSections(
85-
content: string,
86-
): Array<{ id: string; title: string }> {
87-
const sections: Array<{ id: string; title: string }> = [];
88-
// Match ## headings (not ### or deeper)
89-
const headingRegex = /^## (.+)$/gm;
90-
let match;
91-
while ((match = headingRegex.exec(content)) !== null) {
92-
const title = match[1].trim();
93-
// Generate id from title (kebab-case)
94-
const id = title
95-
.toLowerCase()
96-
.replace(/[^a-z0-9\s-]/g, "")
97-
.replace(/\s+/g, "-")
98-
.replace(/-+/g, "-");
99-
if (id) {
100-
sections.push({ id, title });
101-
}
102-
}
103-
return sections;
104-
}
68+
};
10569

10670
// Run the script
10771
rearrangeMdxFilesRecursively(reference);

apps/web/src/app/(docs)/docs/library/page.mdx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const metadata = {
66

77
# Get started with the library
88

9-
This guide will get you up and running with Evolu Library.
9+
This guide will help you get started with the Evolu Library.
1010

1111
<Note>
1212
Requirements: `TypeScript 5.7` or later with the `strict` flag enabled in
@@ -21,32 +21,36 @@ npm install @evolu/common
2121

2222
## Learning path
2323

24-
We recommend learning Evolu Library in this order:
24+
We recommend learning the Evolu Library in this order:
2525

26-
### 1. Result – Error handling
26+
### 1. Array
2727

28-
Start with [`Result`](/docs/api-reference/common/Result/type-aliases/Result), which provides a type-safe way to handle errors without exceptions. It's the foundation for composable error handling throughout Evolu.
28+
Start with [`Array`](/docs/api-reference/common/Array), which provides helpers for improved type-safety and developer experience.
2929

30-
### 2. Task – Asynchronous operations
30+
### 2. Result
3131

32-
Learn [`Task`](/docs/api-reference/common/Task/interfaces/Task), which represents asynchronous computations in a lazy, composable way.
32+
Learn [`Result`](/docs/api-reference/common/Result/type-aliases/Result), a type-safe way to handle errors. It's the foundation for composable error handling.
3333

34-
### 3. Type – Runtime validation
34+
### 3. Task
3535

36-
Understand the [`Type`](/docs/api-reference/common/Type) system for runtime validation and parsing. This enables you to enforce constraints at compile-time and validate untrusted data at runtime.
36+
Continue with [`Task`](/docs/api-reference/common/Task/interfaces/Task) for structured concurrency (promises that can be aborted, monitored, and do not leak).
3737

38-
### 4. Dependency injection
38+
### 4. Type
3939

40-
Explore the [dependency injection pattern](/docs/dependency-injection) used throughout Evolu for decoupled, testable code.
40+
Check the [`Type`](/docs/api-reference/common/Type) to enforce constraints at compile-time and validate data at runtime.
4141

42-
### 5. Resource management
42+
### 5. Dependency injection
4343

44-
Learn [resource management](/docs/resource-management) with `using`, `DisposableStack`, and how it integrates with `Result` for reliable cleanup.
44+
Explore [dependency injection](/docs/dependency-injection), the pattern Evolu uses to swap implementations and simplify testing.
4545

46-
### 6. Conventions
46+
### 6. Resource management
47+
48+
See [resource management](/docs/resource-management)`using`, `DisposableStack`, and how it integrates with `Result` for reliable cleanup.
49+
50+
### 7. Conventions
4751

4852
Review the [Evolu conventions](/docs/conventions) to understand the codebase style and patterns.
4953

5054
## Exploring the API
5155

52-
After understanding the core concepts, explore the full API in the [API reference](/docs/api-reference/common). All code is commented and test files are written to be read as examples—they demonstrate practical usage patterns and edge cases.
56+
After understanding the core concepts, explore the full API in the [API reference](/docs/api-reference/common). All code is commented, and tests are written to be read as examples—they demonstrate practical usage patterns and edge cases.

apps/web/src/app/(docs)/docs/local-first/page.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export const metadata = {
44

55
# Get started with local-first
66

7-
This guide will get you all set up and ready to use Evolu.
7+
This guide will help you get started with the Evolu local-first platform.
88

99
<PlatformSelector />
1010

apps/web/src/app/(docs)/docs/page.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ export const metadata = {
44
"Learn how to use Evolu, whether you're building with the library or the local-first platform.",
55
};
66

7+
export const sections = [];
8+
79
# Documentation
810

911
Evolu is both a **TypeScript library** and a **local-first platform**. Choose your path below.

apps/web/src/components/Features.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ import {
1717
IconLibrary,
1818
IconShieldLock,
1919
IconSql,
20-
TablerIcon,
20+
IconProps,
2121
} from "@tabler/icons-react";
2222

2323
interface Feature {
2424
id: string;
2525
name: string;
2626
description: string;
27-
icon: TablerIcon;
27+
icon: React.ComponentType<IconProps>;
2828
pattern: Omit<
2929
React.ComponentPropsWithoutRef<typeof GridPattern>,
3030
"width" | "height"

apps/web/src/lib/navigation.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ export const navigation: Array<NavGroup> = [
1515
title: "Library",
1616
links: [
1717
{ title: "Getting started", href: "/docs/library" },
18+
{
19+
title: "Array",
20+
href: "/docs/api-reference/common/Array",
21+
},
1822
{
1923
title: "Result",
2024
href: "/docs/api-reference/common/Result/type-aliases/Result",

apps/web/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"next-env.d.ts",
1616
"**/*.ts",
1717
"**/*.tsx",
18+
"**/*.mts",
1819
"next.config.ts",
1920
".next/types/**/*.ts",
2021
"tailwind.config.js"

apps/web/typography.mts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
21
import type { Config } from "tailwindcss";
32

43
const typographyStyles: Config = {

0 commit comments

Comments
 (0)