feat: add fileset resource type support

Add a new "fileset" resource type that represents a collection of files
stored as a relpath→content map. This enables resource types to manage
multiple files (e.g., config directories, template sets) instead of just
a single file.

Backend:
- Add is_fileset column to resource_type table
- Update CRUD operations and workspace duplication to handle is_fileset
- Add integration tests for fileset resource types

Frontend:
- Add FilesetEditor component with file explorer + Monaco editor
- Extract shared FileExplorer component from RawAppSidebar (dedup)
- Add fileset toggle to EditableSchemaWrapper
- Show fileset editor in ResourceEditor and ApiConnectForm
- Show folder icon for fileset resource types in IconedResourceType

CLI:
- Support fileset resources in sync pull (expand to .fileset/ directory)
- Support fileset resources in sync push (reconstruct from directory)
- Handle !inline_fileset YAML tag in resource resolution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-02-23 17:50:37 +00:00
parent 9a7a0135f7
commit 32c4b474f9
575 changed files with 1159 additions and 14866 deletions

View File

@@ -4,7 +4,7 @@
*/
import { expect, test, describe } from "bun:test";
import { deepEqual, isFileResource, toCamel, capitalize } from "../src/utils/utils.ts";
import { deepEqual, isFileResource, isFilesetResource, toCamel, capitalize } from "../src/utils/utils.ts";
import {
getTypeStrFromPath,
removeType,
@@ -156,6 +156,32 @@ describe("isFileResource", () => {
});
});
// =============================================================================
// isFilesetResource
// =============================================================================
describe("isFilesetResource", () => {
test("detects fileset resource paths (unix separator)", () => {
expect(isFilesetResource("f/test/my_config.fileset/config.yaml")).toBe(true);
expect(isFilesetResource("u/admin/templates.fileset/path/to/file.txt")).toBe(true);
});
test("detects fileset resource paths (windows separator)", () => {
expect(isFilesetResource("f\\test\\my_config.fileset\\config.yaml")).toBe(true);
});
test("rejects non-fileset paths", () => {
expect(isFilesetResource("f/test/my_resource.resource.yaml")).toBe(false);
expect(isFilesetResource("f/test/my_file.resource.file.txt")).toBe(false);
expect(isFilesetResource("f/test/my_script.ts")).toBe(false);
});
test("rejects paths ending with .fileset (no child file)", () => {
// The directory itself is not a fileset resource file - only children are
expect(isFilesetResource("f/test/my_config.fileset")).toBe(false);
});
});
// =============================================================================
// removeType
// =============================================================================
@@ -241,6 +267,11 @@ describe("getTypeStrFromPath", () => {
expect(getTypeStrFromPath("devs.group.yaml")).toBe("group");
});
test("detects fileset resource files as resource type", () => {
expect(getTypeStrFromPath("f/test/my_config.fileset/config.yaml")).toBe("resource");
expect(getTypeStrFromPath("u/admin/templates.fileset/path/to/file.txt")).toBe("resource");
});
test("throws for unknown type", () => {
expect(() => getTypeStrFromPath("f/test/unknown.xyz.yaml")).toThrow();
});