@symbo.ls/cli 2.34.4 → 2.34.7

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/bin/collab.js CHANGED
@@ -11,7 +11,7 @@ import { stringifyFunctionsForTransport } from '../helpers/transportUtils.js'
11
11
  import { getCurrentProjectData } from '../helpers/apiUtils.js'
12
12
  import { computeCoarseChanges, computeOrdersForTuples, preprocessChanges } from '../helpers/changesUtils.js'
13
13
  import { createFs } from './fs.js'
14
- import { stripOrderFields } from '../helpers/orderUtils.js'
14
+ import { applyOrderFields } from '../helpers/orderUtils.js'
15
15
  import { normalizeKeys } from '../helpers/compareUtils.js'
16
16
  import {
17
17
  augmentProjectWithLocalPackageDependencies,
@@ -174,8 +174,8 @@ export async function startCollab(options) {
174
174
  }
175
175
 
176
176
  async function writeProjectAndFs(fullObj) {
177
- // Avoid persisting ordering metadata into local repository files
178
- const persistedObj = stripOrderFields(fullObj)
177
+ // Apply platform ordering while avoiding persisting `__order` locally
178
+ const persistedObj = applyOrderFields(fullObj)
179
179
  // Keep schema.dependencies consistent and sync dependencies into local package.json
180
180
  try {
181
181
  ensureSchemaDependencies(persistedObj)
@@ -229,7 +229,7 @@ export async function startCollab(options) {
229
229
  try {
230
230
  const { projectPath } = getConfigPaths()
231
231
  await fs.promises.mkdir(path.dirname(projectPath), { recursive: true })
232
- await fs.promises.writeFile(projectPath, JSON.stringify(stripOrderFields(initialData), null, 2))
232
+ await fs.promises.writeFile(projectPath, JSON.stringify(applyOrderFields(initialData), null, 2))
233
233
  } catch (_) {}
234
234
  currentBase = { ...(initialData || {}) }
235
235
 
@@ -289,6 +289,13 @@ export async function startCollab(options) {
289
289
  : preprocessChanges(currentBase || {}, payload?.changes || []).granularChanges
290
290
  if (!Array.isArray(tuples) || !tuples.length) return
291
291
  applyTuples(currentBase, tuples)
292
+ // Apply server-provided ordering metadata so newly added keys don't just
293
+ // append to the end locally.
294
+ let orders = payload?.orders
295
+ if (typeof orders === 'string') {
296
+ try { orders = JSON.parse(orders) } catch (_) {}
297
+ }
298
+ applyOrders(currentBase, orders)
292
299
  // If server omits schema.dependencies updates, ensure it's present locally
293
300
  ensureSchemaDependencies(currentBase)
294
301
  await writeProjectAndFs(currentBase)
package/bin/fetch.js CHANGED
@@ -13,7 +13,7 @@ import { showAuthRequiredMessages } from '../helpers/buildMessages.js'
13
13
  import { loadSymbolsConfig, resolveDistDir } from '../helpers/symbolsConfig.js'
14
14
  import { loadCliConfig, readLock, writeLock, updateLegacySymbolsJson, getConfigPaths } from '../helpers/config.js'
15
15
  import { ensureSchemaDependencies, findNearestPackageJson, syncPackageJsonDependencies } from '../helpers/dependenciesUtils.js'
16
- import { stripOrderFields } from '../helpers/orderUtils.js'
16
+ import { applyOrderFields } from '../helpers/orderUtils.js'
17
17
  const { isObjectLike } = (utils.default || utils)
18
18
 
19
19
  const debugMsg = chalk.dim(
@@ -85,7 +85,8 @@ export const fetchFromCli = async (opts) => {
85
85
  await fs.promises.mkdir(path.dirname(projectPath), { recursive: true })
86
86
  // Ensure schema.dependencies exists for payload.dependencies
87
87
  ensureSchemaDependencies(payload)
88
- await fs.promises.writeFile(projectPath, JSON.stringify(stripOrderFields(payload), null, 2))
88
+ const persisted = applyOrderFields(payload)
89
+ await fs.promises.writeFile(projectPath, JSON.stringify(persisted, null, 2))
89
90
  } catch (e) {
90
91
  console.error(chalk.bold.red('\nError writing file'))
91
92
  if (verbose) console.error(e)
@@ -134,9 +135,9 @@ export const fetchFromCli = async (opts) => {
134
135
  }
135
136
 
136
137
  if (update || force) {
137
- createFs(stripOrderFields(payload), distDir, { update: true, metadata: false })
138
+ createFs(applyOrderFields(payload), distDir, { update: true, metadata: false })
138
139
  } else {
139
- createFs(stripOrderFields(payload), distDir, { metadata: false })
140
+ createFs(applyOrderFields(payload), distDir, { metadata: false })
140
141
  }
141
142
  }
142
143
 
package/bin/sync.js CHANGED
@@ -16,7 +16,7 @@ import { createFs } from './fs.js'
16
16
  import { showAuthRequiredMessages, showBuildErrorMessages } from '../helpers/buildMessages.js'
17
17
  import { loadSymbolsConfig, resolveDistDir } from '../helpers/symbolsConfig.js'
18
18
  import { loadCliConfig, readLock, writeLock, getConfigPaths, updateLegacySymbolsJson } from '../helpers/config.js'
19
- import { stripOrderFields } from '../helpers/orderUtils.js'
19
+ import { applyOrderFields, stripOrderFields } from '../helpers/orderUtils.js'
20
20
  import { stringifyFunctionsForTransport } from '../helpers/transportUtils.js'
21
21
  import {
22
22
  augmentProjectWithLocalPackageDependencies,
@@ -320,7 +320,8 @@ export async function syncProjectChanges(options) {
320
320
 
321
321
  // Apply changes to local files
322
322
  console.log(chalk.dim('Updating local files...'))
323
- await createFs(stripOrderFields(updatedServerData), distDir, { update: true, metadata: false })
323
+ const orderedUpdatedServerData = applyOrderFields(updatedServerData)
324
+ await createFs(orderedUpdatedServerData, distDir, { update: true, metadata: false })
324
325
  console.log(chalk.gray('Local files updated successfully'))
325
326
 
326
327
  console.log(chalk.bold.green('\nProject synced successfully!'))
@@ -336,7 +337,7 @@ export async function syncProjectChanges(options) {
336
337
  })
337
338
  try {
338
339
  const { projectPath } = getConfigPaths()
339
- await fs.promises.writeFile(projectPath, JSON.stringify(stripOrderFields(updatedServerData), null, 2))
340
+ await fs.promises.writeFile(projectPath, JSON.stringify(orderedUpdatedServerData, null, 2))
340
341
  } catch (_) {}
341
342
 
342
343
  } catch (error) {
@@ -34,6 +34,15 @@ function asPlain(obj) {
34
34
  return stripMetaDeep(normalizeKeys(obj || {}))
35
35
  }
36
36
 
37
+ function getPropCaseInsensitive(obj, key) {
38
+ if (!obj || typeof obj !== 'object') return undefined
39
+ // Prefer exact key first to avoid surprises.
40
+ if (Object.prototype.hasOwnProperty.call(obj, key)) return obj[key]
41
+ const lower = typeof key === 'string' ? key.toLowerCase() : key
42
+ if (typeof lower === 'string' && Object.prototype.hasOwnProperty.call(obj, lower)) return obj[lower]
43
+ return obj[key]
44
+ }
45
+
37
46
  function equal(a, b) {
38
47
  // Coarse compare; sufficient for top-level merges
39
48
  try {
@@ -49,8 +58,8 @@ export function computeChangedKeys(base, local, keys = DATA_KEYS) {
49
58
  const b = asPlain(local)
50
59
  // Only consider top-level data keys; ignore 'schema' entirely
51
60
  for (const key of [...keys]) {
52
- const ak = a?.[key]
53
- const bk = b?.[key]
61
+ const ak = getPropCaseInsensitive(a, key)
62
+ const bk = getPropCaseInsensitive(b, key)
54
63
  if (bk === undefined && ak !== undefined) {
55
64
  changed.push(key)
56
65
  continue
@@ -72,15 +81,17 @@ export function computeCoarseChanges(base, local, keys = DATA_KEYS) {
72
81
  const baseSchema = a?.schema || {}
73
82
 
74
83
  for (const typeKey of [...keys]) {
75
- const aSection = a?.[typeKey] || {}
76
- const bSection = b?.[typeKey] || {}
77
- const aSchemaSection = baseSchema?.[typeKey] || {}
84
+ const aSection = getPropCaseInsensitive(a, typeKey) || {}
85
+ const bSection = getPropCaseInsensitive(b, typeKey) || {}
86
+ const aSchemaSection = getPropCaseInsensitive(baseSchema, typeKey) || {}
78
87
 
79
88
  // If sections are not plain objects (or are arrays), fallback to coarse replacement on the section itself
80
89
  const aIsObject = aSection && typeof aSection === 'object' && !Array.isArray(aSection)
81
90
  const bIsObject = bSection && typeof bSection === 'object' && !Array.isArray(bSection)
82
91
  if (!aIsObject || !bIsObject) {
83
- if (b?.hasOwnProperty(typeKey) === false && a?.hasOwnProperty(typeKey) === true) {
92
+ const hasB = Object.prototype.hasOwnProperty.call(b, typeKey) || Object.prototype.hasOwnProperty.call(b, typeKey.toLowerCase())
93
+ const hasA = Object.prototype.hasOwnProperty.call(a, typeKey) || Object.prototype.hasOwnProperty.call(a, typeKey.toLowerCase())
94
+ if (!hasB && hasA) {
84
95
  changes.push(['delete', [typeKey]])
85
96
  } else if (!equal(aSection, bSection)) {
86
97
  changes.push(['update', [typeKey], bSection])
@@ -43,4 +43,71 @@ export function stripOrderFields(input) {
43
43
  return walk(input)
44
44
  }
45
45
 
46
+ /**
47
+ * Apply Symbols ordering metadata (`__order`) to object key insertion order,
48
+ * while removing the `__order` fields from the returned structure.
49
+ *
50
+ * This lets us generate deterministic file output (e.g. index.js exports) that
51
+ * matches the platform ordering without persisting platform metadata into the
52
+ * repository.
53
+ *
54
+ * - Preserves functions and non-plain objects as-is
55
+ * - Clones arrays and plain objects
56
+ * - Handles cycles via WeakMap
57
+ */
58
+ export function applyOrderFields(input) {
59
+ const seen = new WeakMap()
60
+
61
+ function isPlainObject(v) {
62
+ return Object.prototype.toString.call(v) === '[object Object]'
63
+ }
64
+
65
+ function walk(value) {
66
+ if (Array.isArray(value)) {
67
+ if (seen.has(value)) return seen.get(value)
68
+ const out = new Array(value.length)
69
+ seen.set(value, out)
70
+ for (let i = 0; i < value.length; i++) out[i] = walk(value[i])
71
+ return out
72
+ }
73
+
74
+ if (value && typeof value === 'object') {
75
+ // Keep non-plain objects (Date, Map, etc.) as-is
76
+ if (!isPlainObject(value)) return value
77
+ if (seen.has(value)) return seen.get(value)
78
+
79
+ const out = {}
80
+ seen.set(value, out)
81
+
82
+ const order = Array.isArray(value.__order) ? value.__order : null
83
+ const keys = Object.keys(value).filter((k) => k !== '__order')
84
+ const used = new Set()
85
+
86
+ if (order) {
87
+ for (let i = 0; i < order.length; i++) {
88
+ const k = order[i]
89
+ if (k === '__order') continue
90
+ if (Object.prototype.hasOwnProperty.call(value, k) && !used.has(k)) {
91
+ out[k] = walk(value[k])
92
+ used.add(k)
93
+ }
94
+ }
95
+ }
96
+
97
+ for (let i = 0; i < keys.length; i++) {
98
+ const k = keys[i]
99
+ if (used.has(k)) continue
100
+ out[k] = walk(value[k])
101
+ used.add(k)
102
+ }
103
+
104
+ return out
105
+ }
106
+
107
+ return value
108
+ }
109
+
110
+ return walk(input)
111
+ }
112
+
46
113
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/cli",
3
- "version": "2.34.4",
3
+ "version": "2.34.7",
4
4
  "description": "Fetch your Symbols configuration",
5
5
  "main": "bin/fetch.js",
6
6
  "author": "Symbols",
@@ -15,9 +15,9 @@
15
15
  "vpatch": "npm version patch && npm publish"
16
16
  },
17
17
  "dependencies": {
18
- "@symbo.ls/fetch": "^2.34.4",
19
- "@symbo.ls/init": "^2.34.4",
20
- "@symbo.ls/socket": "^2.34.4",
18
+ "@symbo.ls/fetch": "^2.34.7",
19
+ "@symbo.ls/init": "^2.34.7",
20
+ "@symbo.ls/socket": "^2.34.7",
21
21
  "chalk": "^5.4.1",
22
22
  "chokidar": "^4.0.3",
23
23
  "commander": "^13.1.0",
@@ -28,5 +28,5 @@
28
28
  "socket.io-client": "^4.8.1",
29
29
  "v8-compile-cache": "^2.4.0"
30
30
  },
31
- "gitHead": "24af4cb709642223c3fa555fa6fb954570c31b6b"
31
+ "gitHead": "dc0a8d8734cf5720ecad250b62f0eb4ad2b87590"
32
32
  }