datagrok-tools 6.5.9 → 6.5.10

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Datagrok-tools changelog
2
2
 
3
+ ## 6.5.10 (2026-09-11)
4
+
5
+ * `grok s pull` — a bundle file name is capped at 200 bytes plus a digest of the full name, so it stays unique and stable across pulls. A nested view under a long space spells a longer name than a path component may hold (255 bytes on ext4 and NTFS): one TWIG snapshot reached 268 and the write failed with `ENAMETOOLONG`, taking down the whole part rather than the entity — a `--by-namespace` part that only touched the space as a dependency died with it.
6
+
3
7
  ## 6.5.9 (2026-09-11)
4
8
 
5
9
  * `grok s push/migrate` — placement no longer claims an entity the walk refused. Containment is exclusive, so asserting a project's relation to a platform entity takes it away from whatever holds it on the target: migrating a dashboard built on demo data moved `System:DemoFiles` into the migrated space, and every reference to it by name broke stand-wide until its `System` row was restored by hand. The guard covered a space's own `Files` connection but not `System:` connections, personal `Home` shares, package projects or built-in groups; it now defers to the same `untransferableReason` the walk uses.
@@ -16,6 +16,7 @@ exports.resolveTypes = resolveTypes;
16
16
  exports.stripCredentials = stripCredentials;
17
17
  exports.typeOptionsOf = typeOptionsOf;
18
18
  exports.untransferableReason = untransferableReason;
19
+ var _crypto = require("crypto");
19
20
  /// Docs: [Entity export / import](/docs/features/grok-tool/export-import/DESIGN.md)
20
21
 
21
22
  /** Extra listing rules a `--type` alias adds on top of its entity type. */
@@ -344,6 +345,20 @@ function fileNameFor(json) {
344
345
  const cleaned = String(nqName).replace(/[:/\\?*"<>|]+/g, '.').replace(/^\.+|\.+$/g, '');
345
346
  // Windows resolves `NUL.json` (and `COM1.csv.json`) to a device, whatever the extension.
346
347
  const head = cleaned.split('.')[0];
347
- return RESERVED_RE.test(head) ? `${head}-${json.id ?? 'entity'}${cleaned.slice(head.length)}` : cleaned;
348
+ return shorten(RESERVED_RE.test(head) ? `${head}-${json.id ?? 'entity'}${cleaned.slice(head.length)}` : cleaned);
348
349
  }
349
- const RESERVED_RE = /^(NUL|CON|PRN|AUX|COM[1-9]|LPT[1-9])$/i;
350
+ const RESERVED_RE = /^(NUL|CON|PRN|AUX|COM[1-9]|LPT[1-9])$/i;
351
+
352
+ /**
353
+ * A path component caps at 255 bytes on ext4 and NTFS, and a nested view under a long space
354
+ * reaches that: one TWIG snapshot spelled 268. The write is what fails, so the whole part dies
355
+ * rather than the entity. The digest keeps a truncated name unique and stable across pulls.
356
+ */
357
+ const MAX_FILE_NAME = 200;
358
+ function shorten(name) {
359
+ if (Buffer.byteLength(name) <= MAX_FILE_NAME) return name;
360
+ const digest = (0, _crypto.createHash)('sha1').update(name).digest('hex').slice(0, 8);
361
+ let head = name;
362
+ while (Buffer.byteLength(head) > MAX_FILE_NAME - 9) head = head.slice(0, -1);
363
+ return `${head}-${digest}`;
364
+ }
@@ -1,4 +1,5 @@
1
1
  /// Docs: [Entity export / import](/docs/features/grok-tool/export-import/DESIGN.md)
2
+ import {createHash} from 'crypto';
2
3
 
3
4
  export type BytesKind = 'tables' | 'files';
4
5
 
@@ -249,7 +250,22 @@ export function fileNameFor(json: any): string {
249
250
  const cleaned = String(nqName).replace(/[:/\\?*"<>|]+/g, '.').replace(/^\.+|\.+$/g, '');
250
251
  // Windows resolves `NUL.json` (and `COM1.csv.json`) to a device, whatever the extension.
251
252
  const head = cleaned.split('.')[0];
252
- return RESERVED_RE.test(head) ? `${head}-${json.id ?? 'entity'}${cleaned.slice(head.length)}` : cleaned;
253
+ return shorten(RESERVED_RE.test(head) ? `${head}-${json.id ?? 'entity'}${cleaned.slice(head.length)}` : cleaned);
253
254
  }
254
255
 
255
256
  const RESERVED_RE = /^(NUL|CON|PRN|AUX|COM[1-9]|LPT[1-9])$/i;
257
+
258
+ /**
259
+ * A path component caps at 255 bytes on ext4 and NTFS, and a nested view under a long space
260
+ * reaches that: one TWIG snapshot spelled 268. The write is what fails, so the whole part dies
261
+ * rather than the entity. The digest keeps a truncated name unique and stable across pulls.
262
+ */
263
+ const MAX_FILE_NAME = 200;
264
+
265
+ function shorten(name: string): string {
266
+ if (Buffer.byteLength(name) <= MAX_FILE_NAME) return name;
267
+ const digest = createHash('sha1').update(name).digest('hex').slice(0, 8);
268
+ let head = name;
269
+ while (Buffer.byteLength(head) > MAX_FILE_NAME - 9) head = head.slice(0, -1);
270
+ return `${head}-${digest}`;
271
+ }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/datagrok-ai/public.git"
6
6
  },
7
- "version": "6.5.9",
7
+ "version": "6.5.10",
8
8
  "description": "Utility to upload and publish packages to Datagrok",
9
9
  "homepage": "https://github.com/datagrok-ai/public/tree/master/tools#readme",
10
10
  "dependencies": {