-
Notifications
You must be signed in to change notification settings - Fork 0
[Sprint 16] Clear tsc + patch 0.1.1 #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,18 @@ | ||
| import type { FilesystemDisk } from "../contracts/filesystem"; | ||
|
|
||
| /** | ||
| * Bun S3 ACL values accepted by {@link Bun.S3Client} / presign. | ||
| */ | ||
| export type S3Acl = | ||
| | "private" | ||
| | "public-read" | ||
| | "public-read-write" | ||
| | "authenticated-read" | ||
| | "aws-exec-read" | ||
| | "bucket-owner-read" | ||
| | "bucket-owner-full-control" | ||
| | "log-delivery-write"; | ||
|
|
||
| /** | ||
| * Configuration for S3Adapter. | ||
| */ | ||
|
|
@@ -37,7 +50,7 @@ export interface S3AdapterConfig { | |
| /** | ||
| * Default ACL for uploaded files (optional). | ||
| */ | ||
| acl?: "public-read" | "private" | "public-read-write" | "authenticated-read"; | ||
| acl?: S3Acl; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -62,14 +75,20 @@ export interface S3AdapterConfig { | |
| export class S3Adapter implements FilesystemDisk { | ||
| private client: import("bun").S3Client; | ||
| private root: string; | ||
| private defaultAcl?: "public-read" | "private" | "public-read-write" | "authenticated-read"; | ||
| private bucket: string; | ||
| private endpoint?: string; | ||
| private region: string; | ||
| private defaultAcl?: S3Acl; | ||
|
|
||
| constructor(config: S3AdapterConfig) { | ||
| this.bucket = config.bucket; | ||
| this.endpoint = config.endpoint; | ||
| this.region = config.region ?? "us-east-1"; | ||
| this.client = new Bun.S3Client({ | ||
| accessKeyId: config.accessKeyId, | ||
| bucket: config.bucket, | ||
| endpoint: config.endpoint, | ||
| region: config.region, | ||
| region: this.region, | ||
| secretAccessKey: config.secretAccessKey, | ||
| }); | ||
| this.root = config.root ? this.normalizePath(config.root) : ""; | ||
|
|
@@ -445,8 +464,8 @@ export class S3Adapter implements FilesystemDisk { | |
| async url(path: string): Promise<string> { | ||
| const key = this.resolvePath(path); | ||
| // Return S3 URL format | ||
| const endpoint = this.client.endpoint || `https://s3.${this.client.region || "us-east-1"}.amazonaws.com`; | ||
| return `${endpoint}/${this.client.bucket}/${key}`; | ||
| const endpoint = this.endpoint || `https://s3.${this.region}.amazonaws.com`; | ||
| return `${endpoint}/${this.bucket}/${key}`; | ||
|
Comment on lines
+467
to
+468
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Files matching s3-adapter:"
fd -a 's3-adapter\.(ts|js)$' . || true
file="$(fd 's3-adapter\.(ts|js)$' . | head -n 1)"
if [ -n "${file:-}" ]; then
echo "Selected: $file"
wc -l "$file"
echo "Outline:"
ast-grep outline "$file" || true
echo "Relevant lines 430-490:"
sed -n '430,490p' "$file" | nl -ba -v430
fi
echo
echo "S3 key/url related usages:"
rg -n "getObjectUrl|endpoint|bucket|/\\$\\{key\\}|url|sign|presign" "$file" 2>/dev/null || true
echo
echo "Search URL encoding across repo:"
rg -n "encodeURI|encodeURIComponent|URLSearchParams|object keys|objectKey|S3" -S . || trueRepository: nino-ts/filesystem Length of output: 1017 🏁 Script executed: #!/bin/bash
set -euo pipefail
file="./src/adapters/s3-adapter.ts"
echo "Relevant lines 430-490:"
sed -n '430,490p' "$file"
echo
echo "All occurrences of generated URL expression:"
rg -n "endpoint.*bucket|bucket.*key|getObjectUrl|temporary|presigned|\\$\\{key\\}|\\$\\{endpoint\\}" "$file"
echo
echo "Read-only verifier: extract key interpolation expressions and detect simple URI segment encoding pattern"
python3 - <<'PY'
from pathlib import Path
import re
p = Path('./src/adapters/s3-adapter.ts').read_text()
m = re.search(r'const endpoint = this\.endpoint \|\| `https://s3\.\$\{this\.region\}\.amazonaws\.com`;\s*return `[^`]*(?:\$\{\w+\})+`', p)
print("Found endpoint/url expression:", bool(m))
if m:
start = max(0, p.rfind('\n', 0, m.start()) + 1)
line_no = p.count('\n', 0, m.start()) + 1
print(f"line {line_no}: {m.group(0)}")
PYRepository: nino-ts/filesystem Length of output: 2738 URL-encode S3 object keys. S3 keys may contain spaces, 🤖 Prompt for AI AgentsSource: MCP tools |
||
| } | ||
|
|
||
| /** | ||
|
|
@@ -479,7 +498,7 @@ export class S3Adapter implements FilesystemDisk { | |
| options?: { | ||
| contentType?: string; | ||
| contentLength?: number; | ||
| acl?: string; | ||
| acl?: S3Acl; | ||
| }, | ||
| ): Promise<{ | ||
| url: string; | ||
|
|
@@ -491,7 +510,7 @@ export class S3Adapter implements FilesystemDisk { | |
|
|
||
| // Generate presigned URL for PUT upload | ||
| const url = file.presign({ | ||
| acl: options?.acl || this.defaultAcl || "public-read", | ||
| acl: options?.acl ?? this.defaultAcl ?? "public-read", | ||
| expiresIn, | ||
| method: "PUT", | ||
| type: options?.contentType, | ||
|
|
@@ -605,7 +624,7 @@ export class S3Adapter implements FilesystemDisk { | |
| const result = await this.client.list({ prefix }); | ||
| const results: string[] = []; | ||
|
|
||
| for (const obj of result.objects || []) { | ||
| for (const obj of result.contents ?? []) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift Paginate truncated S3 listings.
🤖 Prompt for AI Agents |
||
| const key = obj.key; | ||
| if (!key) { | ||
| continue; | ||
|
|
@@ -630,7 +649,7 @@ export class S3Adapter implements FilesystemDisk { | |
| const parts = relativeKey.split("/"); | ||
| if (parts.length > 1) { | ||
| const dirName = parts[0]; | ||
| if (!results.includes(dirName)) { | ||
| if (dirName !== undefined && !results.includes(dirName)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Preserve nested directory paths. The new undefined guard is safe, but 🤖 Prompt for AI Agents |
||
| results.push(dirName); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,11 @@ | |
| * allowing application code to remain entirely agnostic to where files | ||
| * are physically stored (local disk, cloud, etc.). | ||
| */ | ||
| /** | ||
| * Laravel-style file visibility. | ||
| */ | ||
| export type FileVisibility = "public" | "private"; | ||
|
|
||
| export interface FilesystemDisk { | ||
| /** | ||
| * Determine if a file exists. | ||
|
|
@@ -14,6 +19,14 @@ export interface FilesystemDisk { | |
| */ | ||
| exists(path: string): Promise<boolean>; | ||
|
|
||
| /** | ||
| * Determine if a file is missing (inverse of {@link exists}). | ||
| * | ||
| * @param path - The path to check | ||
| * @returns A promise resolving to true if it does not exist | ||
| */ | ||
| missing(path: string): Promise<boolean>; | ||
|
|
||
| /** | ||
| * Get the contents of a file. | ||
| * | ||
|
|
@@ -130,15 +143,24 @@ export interface FilesystemDisk { | |
| */ | ||
| append(path: string, data: string): Promise<boolean>; | ||
|
|
||
| /** | ||
| * Prepend data to a file. | ||
| * | ||
| * @param path - The file path | ||
| * @param data - The data to prepend | ||
| * @returns A promise resolving to true on success | ||
| */ | ||
| prepend(path: string, data: string): Promise<boolean>; | ||
|
|
||
| /** | ||
| * Get the visibility of a file (public/private). | ||
| */ | ||
| getVisibility(path: string): Promise<string | null>; | ||
| getVisibility(path: string): Promise<FileVisibility | null>; | ||
|
Comment on lines
155
to
+158
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== locate filesystem-related files =="
git ls-files | rg 'src/contracts/filesystem\.ts$|filesystem|storage|s3|adapter|storage' | head -200
echo
echo "== outline src/contracts/filesystem.ts =="
if [ -f src/contracts/filesystem.ts ]; then
ast-grep outline src/contracts/filesystem.ts || true
wc -l src/contracts/filesystem.ts
fi
echo
echo "== relevant contract section =="
if [ -f src/contracts/filesystem.ts ]; then
sed -n '120,190p' src/contracts/filesystem.ts
fi
echo
echo "== search getVisibility/setVisibility implementations/usages =="
rg -n "getVisibility|setVisibility|FileVisibility|setPublic|SetPublic|FilesystemDisk" src || trueRepository: nino-ts/filesystem Length of output: 10735 🏁 Script executed: # The previous shell was a capability request only; results are not present.Repository: nino-ts/filesystem Length of output: 156 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== S3 adapter visibility methods =="
sed -n '360,450p' src/adapters/s3-adapter.ts
echo
echo "== Memory adapter visibility methods =="
sed -n '260,320p' src/adapters/memory-adapter.ts
echo
echo "== Local adapter visibility methods =="
sed -n '220,255p' src/adapters/local-adapter.ts
echo
echo "== S3 tests visibility-related content =="
rg -n -C 5 "getVisibility|setVisibility|visibility|acl|aclObject|ACL" tests/unit/s3-adapter.test.ts
echo
echo "== package deps relevant =="
if [ -f package.json ]; then jq '.dependencies, .devDependencies | {aws, s3}' package.json; fiRepository: nino-ts/filesystem Length of output: 7713 🏁 Script executed: # The previous shell was a capability request only; results are not present.Repository: nino-ts/filesystem Length of output: 156 Make S3 visibility methods honor the visibility contract.
🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
| * Set the visibility of a file. | ||
| */ | ||
| setVisibility(path: string, visibility: string): Promise<boolean>; | ||
| setVisibility(path: string, visibility: FileVisibility): Promise<boolean>; | ||
|
|
||
| /** | ||
| * Get the MIME type of a file. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,10 @@ | ||
| import type { FilesystemDisk } from "../contracts/filesystem"; | ||
|
|
||
| /** | ||
| * Bun gzip compression level (zlib / libdeflate). | ||
| */ | ||
| export type GzipLevel = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; | ||
|
|
||
|
Comment on lines
+3
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
curl -fsSL https://bun.sh/docs/runtime/utils | grep -F 'Must be between `-1` and `9`'
rg -n -C2 'type GzipLevel|gzipSync' src/decorators/compressed-adapter.tsRepository: nino-ts/filesystem Length of output: 50375 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== relevant occurrences =="
rg -n -C3 'type GzipLevel|gzipSync|gunzipSync|level.*GzipLevel|GzipLevel' src/decorators/compressed-adapter.ts
echo
echo "== targeted docs search =="
curl -fsSL https://bun.sh/docs/runtime/utils | sed -n '/gzipSync/,+60p' | sed -n '1,120p'
echo
echo "== file section =="
sed -n '1,90p;120,170p' src/decorators/compressed-adapter.tsRepository: nino-ts/filesystem Length of output: 50375 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== relevant occurrences =="
rg -n -C3 'type GzipLevel|gzipSync|gunzipSync|level.*GzipLevel|GzipLevel' src/decorators/compressed-adapter.ts
echo
echo "== targeted docs search =="
curl -fsSL https://bun.sh/docs/runtime/utils | sed -n '/gzipSync/,+80p' | sed -n '1,160p'
echo
echo "== file section =="
sed -n '1,100p;120,180p' src/decorators/compressed-adapter.tsRepository: nino-ts/filesystem Length of output: 50375 🌐 Web query:
💡 Result: The level option for Bun.gzipSync must be between -1 and 9 [1][2][3]. According to the official Bun documentation, the compression level is defined within the ZlibCompressionOptions interface as follows: - A value of -1 uses the default compression level (currently 6) [1][2][3]. - A value of 0 provides no compression [1][2][3]. - A value of 1 offers the least compression and the fastest speed [1][2][3]. - A value of 9 offers the best compression and the slowest speed [1][2][3]. The valid range for the level option is -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, or 9 [1][3]. Citations:
Restrict
🤖 Prompt for AI Agents |
||
| /** | ||
| * Options for CompressedAdapter. | ||
| */ | ||
|
|
@@ -17,10 +22,10 @@ export interface CompressedAdapterOptions { | |
| minSize?: number; | ||
|
|
||
| /** | ||
| * Compression level (1-9). | ||
| * Compression level (1-9 zlib; up to 12 for libdeflate). | ||
| * @default 6 | ||
| */ | ||
| level?: number; | ||
| level?: GzipLevel; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -45,7 +50,7 @@ export class CompressedAdapter implements FilesystemDisk { | |
| private adapter: FilesystemDisk; | ||
| private extensions?: string[]; | ||
| private minSize: number; | ||
| private level: number; | ||
| private level: GzipLevel; | ||
|
|
||
| constructor(adapter: FilesystemDisk, options: CompressedAdapterOptions = {}) { | ||
| this.adapter = adapter; | ||
|
|
@@ -101,7 +106,7 @@ export class CompressedAdapter implements FilesystemDisk { | |
| const data = await this.adapter.get(compressedPath); | ||
| if (data) { | ||
| try { | ||
| const decompressed = await Bun.gunzip(data); | ||
| const decompressed = Bun.gunzipSync(data); | ||
| return new TextDecoder().decode(decompressed); | ||
|
Comment on lines
106
to
110
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Repo files around candidate names:"
fd -a 'compressed-adapter|archive|filesystem' . | sed 's#^\./##' | head -200
echo
echo "Inspect compressed-adapter outline:"
ast-grep outline src/decorators/compressed-adapter.ts --view expanded || true
echo
echo "Compressed-adapter relevant lines:"
cat -n src/decorators/compressed-adapter.ts | sed -n '1,160p'
echo
echo "Archive outline:"
ast-grep outline src/utils/archive.ts --view expanded || true
echo
echo "Archive relevant lines:"
cat -n src/utils/archive.ts | sed -n '1,150p'Repository: nino-ts/filesystem Length of output: 14360 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Filesystem contract:"
cat -n src/contracts/filesystem.ts | sed -n '1,260p'
echo
echo "Search for FilesystemDisk get/getBytes/getBytes/bytes implementations and related adapters:"
rg -n "interface FilesystemDisk|class .*FilesystemDisk|implements FilesystemDisk|get\\(|getBytes\\(|bytes\\(|async get\\(|Promise\\.Uint8Array|Uint8Array|string \\| null" src -S
echo
echo "Try detecting static type shape:"
python3 - <<'PY'
from pathlib import Path
for p in Path("src").rglob("*.ts"):
s=p.read_text()
if "class " in s or "interface FilesystemDisk" in s:
print(f"\n--- {p} ---")
for i,line in enumerate(s.splitlines(),1):
if "get(" in line or "interface FilesystemDisk" in line or "implements FilesystemDisk" in line or "bytes(" in line or "getBytes(" in line or "Promise<" in line:
print(f"{i}: {line[:220]}")
PY
echo
echo "Node text binary probe matching compressor flow:"
node - <<'JS'
const zlib = require('zlib');
const data = new Uint8Array([0xCA, 0xFE, 0xBA, 0xBE, 0xFF]);
const compressed = zlib.gzipSync(data);
const text = Buffer.from(compressed).toString('utf8');
const input = Buffer.from(text, 'utf8');
const decompressed = zlib.gunzipSync(input);
console.log(JSON.stringify({
original: Array.from(data),
textBytes: Array.from(input),
decompressed: Array.from(decompressed),
equalBufferToStringBuffer: Buffer.compare(compressed, input) === 0,
equalOriginalToDecompressed: Buffer.compare(data, decompressed) === 0,
}));
JSRepository: nino-ts/filesystem Length of output: 36635 Add a byte-exact filesystem read path. 📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| } catch { | ||
| // If decompression fails, return as-is | ||
|
|
@@ -134,7 +139,7 @@ export class CompressedAdapter implements FilesystemDisk { | |
| } | ||
|
|
||
| // Compress and write | ||
| const compressed = await Bun.gzip(data, { level: this.level }); | ||
| const compressed = Bun.gzipSync(new Uint8Array(data), { level: this.level }); | ||
| return this.adapter.put(compressedPath, compressed); | ||
| } catch (_error) { | ||
| return false; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -127,25 +127,31 @@ export class WatchedAdapter implements FilesystemDisk { | |
| */ | ||
| private createWatcher(path: string): void { | ||
| try { | ||
| let debounceTimer: ReturnType<typeof setTimeout> | null = null; | ||
|
|
||
| const watcher = watch(path, { recursive: this.recursive }, (event, filename) => { | ||
| if (!filename) { | ||
| return; | ||
| } | ||
|
|
||
| // Debounce | ||
| const callbacks = this.callbacks.get(path); | ||
| if (!callbacks) { | ||
| return; | ||
| } | ||
|
|
||
| const eventType = event === "change" ? "change" : "rename"; | ||
|
|
||
| // Call all callbacks | ||
| for (const callback of callbacks) { | ||
| try { | ||
| callback(eventType, filename); | ||
| } catch (_error) {} | ||
| if (debounceTimer !== null) { | ||
| clearTimeout(debounceTimer); | ||
| } | ||
|
|
||
| debounceTimer = setTimeout(() => { | ||
| for (const callback of callbacks) { | ||
| try { | ||
| callback(eventType, filename); | ||
| } catch (_error) {} | ||
| } | ||
| }, this.debounce); | ||
|
Comment on lines
+130
to
+154
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Debounce per filename and cancel pending delivery on unwatch. One timer per watched directory drops earlier events when different files change within the debounce window. It also survives 🤖 Prompt for AI Agents |
||
| }); | ||
|
|
||
| this.watchers.set(path, watcher); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Serialize prepend writes to prevent lost updates.
get()followed byput()is not atomic. Concurrentprepend()calls can read the same old contents, then the last write silently discards the other prefix. Add per-path serialization shared with other write operations, or use an atomic filesystem primitive.🤖 Prompt for AI Agents