goldsync 0.1.14 → 0.1.15
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.
- package/README.md +4 -0
- package/assets/GoldSync.rbxmx +29 -13
- package/package.json +1 -1
- package/src/config.mjs +22 -2
- package/src/server.mjs +1 -0
- package/src/store.mjs +6 -1
package/README.md
CHANGED
|
@@ -73,6 +73,10 @@ Place `goldsync.project.json` at the game repository root:
|
|
|
73
73
|
|
|
74
74
|
A `file` root becomes one RBXM. A `directory` root creates separate files for descendants at `splitDepth`. Split at complete models, effects, or UI components—not individual parts or attachments. Never map the same hierarchy in Rojo, and do not include scripts inside a GoldSync root.
|
|
75
75
|
|
|
76
|
+
Directory roots can contain more specific roots. Discovery skips branches owned by those roots, including existing files on disk. For example, split `ReplicatedStorage.Assets` at depth 1 and add another depth-1 directory root for `ReplicatedStorage.Assets.Effects`. New asset categories and individual effects are discovered automatically, without creating an overlapping `Effects.rbxm`. Existing explicit file roots can stay in place to preserve their sync history. File roots cannot contain other roots because their snapshots include the entire subtree.
|
|
77
|
+
|
|
78
|
+
When upgrading an existing project, put the new parent directory roots in the optional top-level `discoveryRoots` array, using the same fields as directory roots. Older GoldSync versions ignore that array, so they cannot create overlapping snapshots before the service is restarted with nested-root support. Existing roots and files remain unchanged.
|
|
79
|
+
|
|
76
80
|
GoldSync reads the Rojo project name and fallback `servePort` from `default.project.json`. It automatically detects `rojo serve --port` overrides and verifies the listener's project name before connecting. That active Rojo session selects the GoldSync workspace, so Studio place IDs and names do not need configuration. Set `rojo.projectFile` only when the repository uses another project file.
|
|
77
81
|
|
|
78
82
|
Map GoldSync's generated live-session marker in the Rojo project tree:
|
package/assets/GoldSync.rbxmx
CHANGED
|
@@ -22,7 +22,7 @@ local CLIENT_HEADERS = {
|
|
|
22
22
|
}
|
|
23
23
|
local SETTINGS_PREFIX = "GoldSync:v2:"
|
|
24
24
|
local UI_SETTINGS_PREFIX = "GoldSync:ui:v1:"
|
|
25
|
-
local VERSION = "0.1.
|
|
25
|
+
local VERSION = "0.1.15"
|
|
26
26
|
local REQUEST_INTERVAL_SECONDS = 0.15
|
|
27
27
|
local REQUEST_RETRY_DELAYS = { 1, 2, 4 }
|
|
28
28
|
|
|
@@ -527,6 +527,24 @@ local function discoverSplitRoot(splitRoot, root)
|
|
|
527
527
|
end
|
|
528
528
|
local missingPaths = {}
|
|
529
529
|
for _, relativePath in collectSplitPaths(root, splitRoot.splitDepth) do
|
|
530
|
+
local excluded = false
|
|
531
|
+
for _, excludedPath in splitRoot.excludedPaths or {} do
|
|
532
|
+
local matches = true
|
|
533
|
+
for index, segment in excludedPath do
|
|
534
|
+
if relativePath[index] ~= segment then
|
|
535
|
+
matches = false
|
|
536
|
+
break
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
if matches then
|
|
540
|
+
excluded = true
|
|
541
|
+
break
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
if excluded then
|
|
545
|
+
continue
|
|
546
|
+
end
|
|
547
|
+
|
|
530
548
|
local fullPath = table.clone(splitRoot.studioPath)
|
|
531
549
|
for _, segment in relativePath do
|
|
532
550
|
table.insert(fullPath, segment)
|
|
@@ -1553,14 +1571,6 @@ local function createGroup(path, order)
|
|
|
1553
1571
|
section.LayoutOrder = order
|
|
1554
1572
|
section.Size = UDim2.new(1, 0, 0, 0)
|
|
1555
1573
|
section.Parent = if parentGroup then parentGroup.list else rootsContainer
|
|
1556
|
-
if parentGroup then
|
|
1557
|
-
local connector = Instance.new("Frame")
|
|
1558
|
-
connector.BackgroundColor3 = Color3.fromRGB(72, 72, 82)
|
|
1559
|
-
connector.BorderSizePixel = 0
|
|
1560
|
-
connector.Position = UDim2.new(0, -8, 0, 11)
|
|
1561
|
-
connector.Size = UDim2.fromOffset(35, 1)
|
|
1562
|
-
connector.Parent = section
|
|
1563
|
-
end
|
|
1564
1574
|
|
|
1565
1575
|
local sectionLayout = Instance.new("UIListLayout")
|
|
1566
1576
|
sectionLayout.Padding = UDim.new(0, 2)
|
|
@@ -1573,6 +1583,14 @@ local function createGroup(path, order)
|
|
|
1573
1583
|
header.LayoutOrder = 1
|
|
1574
1584
|
header.Size = UDim2.new(1, 0, 0, 24)
|
|
1575
1585
|
header.Parent = section
|
|
1586
|
+
if parentGroup then
|
|
1587
|
+
local connector = Instance.new("Frame")
|
|
1588
|
+
connector.BackgroundColor3 = Color3.fromRGB(72, 72, 82)
|
|
1589
|
+
connector.BorderSizePixel = 0
|
|
1590
|
+
connector.Position = UDim2.new(0, -8, 0, 11)
|
|
1591
|
+
connector.Size = UDim2.fromOffset(35, 1)
|
|
1592
|
+
connector.Parent = header
|
|
1593
|
+
end
|
|
1576
1594
|
|
|
1577
1595
|
local disclosure = Instance.new("TextButton")
|
|
1578
1596
|
disclosure.BackgroundTransparency = 1
|
|
@@ -1865,7 +1883,6 @@ local function createInstanceNode(instance, parent, depth, order)
|
|
|
1865
1883
|
connector.BorderSizePixel = 0
|
|
1866
1884
|
connector.Position = UDim2.new(0, -8, 0, 11)
|
|
1867
1885
|
connector.Size = UDim2.fromOffset(35, 1)
|
|
1868
|
-
connector.Parent = section
|
|
1869
1886
|
|
|
1870
1887
|
local header = Instance.new("Frame")
|
|
1871
1888
|
header.BackgroundColor3 = Color3.fromRGB(40, 40, 46)
|
|
@@ -1873,6 +1890,7 @@ local function createInstanceNode(instance, parent, depth, order)
|
|
|
1873
1890
|
header.LayoutOrder = 1
|
|
1874
1891
|
header.Size = UDim2.new(1, 0, 0, 22)
|
|
1875
1892
|
header.Parent = section
|
|
1893
|
+
connector.Parent = header
|
|
1876
1894
|
header.MouseEnter:Connect(function()
|
|
1877
1895
|
header.BackgroundTransparency = 0
|
|
1878
1896
|
end)
|
|
@@ -2033,7 +2051,6 @@ local function createRootRow(manifest, order)
|
|
|
2033
2051
|
connector.BorderSizePixel = 0
|
|
2034
2052
|
connector.Position = UDim2.new(0, -8, 0, 11)
|
|
2035
2053
|
connector.Size = UDim2.fromOffset(35, 1)
|
|
2036
|
-
connector.Parent = row
|
|
2037
2054
|
|
|
2038
2055
|
local corner = Instance.new("UICorner")
|
|
2039
2056
|
corner.CornerRadius = UDim.new(0, 6)
|
|
@@ -2041,8 +2058,6 @@ local function createRootRow(manifest, order)
|
|
|
2041
2058
|
|
|
2042
2059
|
local rowPadding = Instance.new("UIPadding")
|
|
2043
2060
|
rowPadding.PaddingBottom = UDim.new(0, 5)
|
|
2044
|
-
rowPadding.PaddingLeft = UDim.new(0, 6)
|
|
2045
|
-
rowPadding.PaddingRight = UDim.new(0, 6)
|
|
2046
2061
|
rowPadding.PaddingTop = UDim.new(0, 5)
|
|
2047
2062
|
rowPadding.Parent = row
|
|
2048
2063
|
|
|
@@ -2057,6 +2072,7 @@ local function createRootRow(manifest, order)
|
|
|
2057
2072
|
titleLine.LayoutOrder = 1
|
|
2058
2073
|
titleLine.Size = UDim2.new(1, 0, 0, 22)
|
|
2059
2074
|
titleLine.Parent = row
|
|
2075
|
+
connector.Parent = titleLine
|
|
2060
2076
|
|
|
2061
2077
|
local contentDisclosure = Instance.new("TextButton")
|
|
2062
2078
|
contentDisclosure.BackgroundTransparency = 1
|
package/package.json
CHANGED
package/src/config.mjs
CHANGED
|
@@ -86,6 +86,10 @@ export async function loadConfig(configPath) {
|
|
|
86
86
|
if (!Array.isArray(raw.roots) || raw.roots.length === 0) {
|
|
87
87
|
throw new Error("roots must contain at least one sync root");
|
|
88
88
|
}
|
|
89
|
+
if (raw.discoveryRoots !== undefined && (!Array.isArray(raw.discoveryRoots)
|
|
90
|
+
|| raw.discoveryRoots.some((root) => !root || root.splitDepth === undefined))) {
|
|
91
|
+
throw new Error("discoveryRoots must contain directory roots with splitDepth");
|
|
92
|
+
}
|
|
89
93
|
const maxPathDepth = raw.maxPathDepth ?? 3;
|
|
90
94
|
if (!Number.isInteger(maxPathDepth) || maxPathDepth < 2 || maxPathDepth > 10) {
|
|
91
95
|
throw new Error("maxPathDepth must be an integer between 2 and 10");
|
|
@@ -95,8 +99,8 @@ export async function loadConfig(configPath) {
|
|
|
95
99
|
const studioPaths = new Set();
|
|
96
100
|
const files = new Set();
|
|
97
101
|
const splitRoots = [];
|
|
98
|
-
const roots = raw.roots.flatMap((root, index) => {
|
|
99
|
-
const label = `roots[${index}]`;
|
|
102
|
+
const roots = [...raw.roots, ...(raw.discoveryRoots ?? [])].flatMap((root, index) => {
|
|
103
|
+
const label = index < raw.roots.length ? `roots[${index}]` : `discoveryRoots[${index - raw.roots.length}]`;
|
|
100
104
|
const id = requireString(root.id, `${label}.id`);
|
|
101
105
|
if (!ID_PATTERN.test(id)) {
|
|
102
106
|
throw new Error(`${label}.id must contain lowercase letters, numbers, or hyphens`);
|
|
@@ -145,6 +149,22 @@ export async function loadConfig(configPath) {
|
|
|
145
149
|
}];
|
|
146
150
|
});
|
|
147
151
|
|
|
152
|
+
const configuredRoots = [...roots, ...splitRoots];
|
|
153
|
+
for (const root of roots) {
|
|
154
|
+
if (configuredRoots.some((other) => other !== root
|
|
155
|
+
&& other.studioPath.length > root.studioPath.length
|
|
156
|
+
&& root.studioPath.every((segment, index) => other.studioPath[index] === segment))) {
|
|
157
|
+
throw new Error(`file root ${root.id} contains another sync root; use a directory root instead`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (const root of splitRoots) {
|
|
161
|
+
root.excludedPaths = configuredRoots
|
|
162
|
+
.filter((other) => other !== root
|
|
163
|
+
&& other.studioPath.length > root.studioPath.length
|
|
164
|
+
&& root.studioPath.every((segment, index) => other.studioPath[index] === segment))
|
|
165
|
+
.map((other) => other.studioPath.slice(root.studioPath.length, root.studioPath.length + root.splitDepth));
|
|
166
|
+
}
|
|
167
|
+
|
|
148
168
|
return {
|
|
149
169
|
version: 1,
|
|
150
170
|
name: requireString(raw.name, "name"),
|
package/src/server.mjs
CHANGED
|
@@ -161,6 +161,7 @@ export class GoldSyncServer {
|
|
|
161
161
|
id: root.id,
|
|
162
162
|
studioPath: root.studioPath,
|
|
163
163
|
splitDepth: root.splitDepth,
|
|
164
|
+
excludedPaths: root.excludedPaths ?? [],
|
|
164
165
|
})),
|
|
165
166
|
roots: this.store.getManifests().map((manifest) => {
|
|
166
167
|
const conflict = conflicts.get(manifest.id);
|
package/src/store.mjs
CHANGED
|
@@ -140,7 +140,12 @@ export class SnapshotStore {
|
|
|
140
140
|
}
|
|
141
141
|
let changed = false;
|
|
142
142
|
for (const relativePath of relativePaths) {
|
|
143
|
-
|
|
143
|
+
const root = this.makeSplitRoot(splitRoot, relativePath);
|
|
144
|
+
if ((splitRoot.excludedPaths ?? []).some((excluded) =>
|
|
145
|
+
excluded.every((segment, index) => relativePath[index] === segment))) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
changed = (await this.addRoot(root)) || changed;
|
|
144
149
|
}
|
|
145
150
|
if (changed) {
|
|
146
151
|
await this.persistState();
|