@getguru/import-engine 0.1.0 → 0.1.2

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 CHANGED
@@ -5,7 +5,7 @@ Google Sites, Google Docs, Word, PDF, Slab, GitHub wiki, Markdown, spreadsheet)
5
5
  that Guru can import. It is the same pipeline as the webapp's "Import content" screen,
6
6
  built from `guru-labs`.
7
7
 
8
- Requires Node >=22.
8
+ Requires Node >=22.12.
9
9
 
10
10
  ## Usage
11
11
 
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
2
  var __create = Object.create;
4
3
  var __defProp = Object.defineProperty;
5
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -114321,7 +114320,7 @@ var import_jszip5 = __toESM(require_lib3());
114321
114320
  var import_url_join6 = __toESM(require_url_join());
114322
114321
 
114323
114322
  // packages/import-engine/package.json
114324
- var version = "0.1.0";
114323
+ var version = "0.1.2";
114325
114324
 
114326
114325
  // src/docx-import/detectors.js
114327
114326
  function isGoogleSitesDoc(div) {
@@ -114345,7 +114344,7 @@ var showError;
114345
114344
  var updateLoader;
114346
114345
  var showReport;
114347
114346
  var docLinkIndex = /* @__PURE__ */ new Map();
114348
- var toolVersion = true ? `${version}+${"66b3d49"}` : version;
114347
+ var toolVersion = true ? `${version}+${"4d61d22"}` : version;
114349
114348
  var pendingUpload = null;
114350
114349
  function confirmPendingUpload() {
114351
114350
  const upload = pendingUpload;
@@ -115156,6 +115155,91 @@ function setSubtract(a, b) {
115156
115155
  return !b.includes(value);
115157
115156
  });
115158
115157
  }
115158
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
115159
+ var EXTERNAL_MANIFEST_MAX_BYTES = 1048576;
115160
+ var EXTERNAL_MAX_ENTRIES = 500;
115161
+ var EXTERNAL_NAME_RE = /^[\p{L}\p{N}._-]{1,128}$/u;
115162
+ var EXTERNAL_REFUSED = "the server won't fetch this image; the card will link to the original URL";
115163
+ function checkExternalImages(zip, task, report) {
115164
+ var state = { enabled: false, names: /* @__PURE__ */ new Set(), referenced: [] };
115165
+ report.externalImages = state;
115166
+ var collectionFile = zip.file("collection.yaml");
115167
+ var manifestFile = zip.file("resources/external.json");
115168
+ function readCollection() {
115169
+ if (!collectionFile) {
115170
+ return Promise.resolve();
115171
+ }
115172
+ return collectionFile.async("string").then(function(yamlString) {
115173
+ try {
115174
+ var collection = yaml.load(yamlString);
115175
+ state.enabled = !!collection && typeof collection == "object" && collection.FetchExternalImages === true;
115176
+ } catch (e) {
115177
+ }
115178
+ }, function() {
115179
+ });
115180
+ }
115181
+ function readManifest() {
115182
+ if (!manifestFile) {
115183
+ return Promise.resolve();
115184
+ }
115185
+ return manifestFile.async("string").then(function(text2) {
115186
+ var size2 = manifestFile._data && manifestFile._data.uncompressedSize || text2.length;
115187
+ var dropped = "The server will ignore the whole manifest, so no external images will be fetched";
115188
+ if (size2 > EXTERNAL_MANIFEST_MAX_BYTES) {
115189
+ report.error(`resources/external.json is larger than ${EXTERNAL_MANIFEST_MAX_BYTES} bytes. ${dropped}.`);
115190
+ return;
115191
+ }
115192
+ var manifest;
115193
+ try {
115194
+ manifest = JSON.parse(text2);
115195
+ } catch (e) {
115196
+ report.error(`resources/external.json is not valid JSON (${e.message}). ${dropped}.`);
115197
+ return;
115198
+ }
115199
+ if (!manifest || typeof manifest != "object" || Array.isArray(manifest)) {
115200
+ report.error(`resources/external.json must be a JSON object mapping file names to URLs. ${dropped}.`);
115201
+ return;
115202
+ }
115203
+ var keys2 = Object.keys(manifest);
115204
+ if (keys2.some(function(key) {
115205
+ return typeof manifest[key] != "string";
115206
+ })) {
115207
+ report.error(`resources/external.json must map every file name to a URL string. ${dropped}.`);
115208
+ return;
115209
+ }
115210
+ keys2.forEach(function(name, index) {
115211
+ state.names.add(name);
115212
+ var url = manifest[name];
115213
+ var label = `resources/external.json entry "${escapeQuotes(name)}"`;
115214
+ if (index >= EXTERNAL_MAX_ENTRIES) {
115215
+ report.warn(`${label} is past the first ${EXTERNAL_MAX_ENTRIES} entries: ${EXTERNAL_REFUSED}.`);
115216
+ } else if (!EXTERNAL_NAME_RE.test(name) || name.includes("..")) {
115217
+ report.warn(`${label} has an invalid file name: ${EXTERNAL_REFUSED}.`);
115218
+ } else if (url.trim() === "") {
115219
+ report.warn(`${label} has a blank URL: ${EXTERNAL_REFUSED}.`);
115220
+ } else if (!/^https:\/\//i.test(url.trim())) {
115221
+ report.warn(`${label} is not an https URL: ${EXTERNAL_REFUSED}.`);
115222
+ }
115223
+ if (zip.file("resources/" + name)) {
115224
+ report.info(`${label} also exists as resources/${escapeQuotes(name)} in the zip, so the server skips the fetch.`);
115225
+ }
115226
+ });
115227
+ }, function() {
115228
+ });
115229
+ }
115230
+ return readCollection().then(readManifest).then(function() {
115231
+ if (manifestFile && !state.enabled) {
115232
+ report.warn(`resources/external.json is included but collection.yaml doesn't set FetchExternalImages: true, so the server ignores it.`);
115233
+ }
115234
+ if (state.enabled && !manifestFile) {
115235
+ report.warn(`collection.yaml sets FetchExternalImages: true but the zip has no resources/external.json.`);
115236
+ }
115237
+ if (state.enabled) {
115238
+ report.syncError(`If this is a sync, FetchExternalImages is ignored (external images are only fetched on imports).`);
115239
+ }
115240
+ report.next();
115241
+ });
115242
+ }
115159
115243
  function escapeQuotes(value) {
115160
115244
  value = value || "";
115161
115245
  return value.replace(/"/g, `\\"`);
@@ -115190,11 +115274,18 @@ function checkCardYaml(zip, task, report) {
115190
115274
  return zip.file(task.filename).async("string").then(function(yamlString) {
115191
115275
  try {
115192
115276
  var card = yaml.load(yamlString);
115193
- var possibleKeys = ["ExternalId", "ExternalUrl", "Title", "Tags", "externalLastUpdated"];
115277
+ var possibleKeys = ["ExternalId", "ExternalUrl", "Title", "Tags", "externalLastUpdated", "ID"];
115194
115278
  var otherKeys = setSubtract(Object.keys(card), possibleKeys);
115195
115279
  otherKeys.forEach(function(key) {
115196
115280
  report.warn(`Unexpected property "${key}" in the card's yaml.`);
115197
115281
  });
115282
+ if (card.ID !== void 0 && card.ID !== null) {
115283
+ if (typeof card.ID != "string" || !UUID_RE2.test(card.ID)) {
115284
+ report.error(`The card's ID property must be a UUID string (e.g. ID: "123e4567-e89b-12d3-a456-426614174000"), but it was: ${escapeHtml3(String(card.ID))}`);
115285
+ } else {
115286
+ report.info(`The card has an ID, so it updates the existing card with that ID in the same collection if one exists, otherwise a new card is created.`);
115287
+ }
115288
+ }
115198
115289
  if (!card.Title) {
115199
115290
  report.error(`A "Title" property is required in the card's yaml.`);
115200
115291
  } else {
@@ -115259,7 +115350,17 @@ function checkCardHtml(zip, task, report) {
115259
115350
  report.resourceReferences.push(reference2);
115260
115351
  }
115261
115352
  } else {
115262
- report.error(`The referenced resource "${reference2}" does not exist in the zip.`);
115353
+ var ext = report.externalImages || { enabled: false, names: /* @__PURE__ */ new Set(), referenced: [] };
115354
+ var extName = reference2.slice("resources/".length);
115355
+ if (ext.names.has(extName) && ext.enabled) {
115356
+ if (!ext.referenced.includes(extName)) {
115357
+ ext.referenced.push(extName);
115358
+ }
115359
+ } else if (ext.names.has(extName)) {
115360
+ report.error(`The referenced resource "${reference2}" is listed in resources/external.json but collection.yaml doesn't set FetchExternalImages: true, so it won't be fetched.`);
115361
+ } else {
115362
+ report.error(`The referenced resource "${reference2}" does not exist in the zip.`);
115363
+ }
115263
115364
  }
115264
115365
  } else if (reference2.startsWith("cards/")) {
115265
115366
  if (zip.file(reference2 + ".yaml")) {
@@ -115342,7 +115443,7 @@ function checkBoardYaml(zip, task, report) {
115342
115443
  }
115343
115444
  }
115344
115445
  if (board.Items) {
115345
- let checkCard2 = function(card) {
115446
+ let checkCard = function(card) {
115346
115447
  var possibleKeys = ["Type", "ID"];
115347
115448
  var otherKeys = setSubtract(Object.keys(card), possibleKeys);
115348
115449
  otherKeys.forEach(function(key) {
@@ -115358,7 +115459,7 @@ function checkBoardYaml(zip, task, report) {
115358
115459
  if (!zip.files[`cards/${card.ID}.yaml`]) {
115359
115460
  report.error(`This board references a card (ID = "${card.ID}") that does not seem to exist in this zip, we expect cards/${card.ID}.yaml would also exist.`);
115360
115461
  }
115361
- }, checkSection2 = function(section) {
115462
+ }, checkSection = function(section) {
115362
115463
  var possibleKeys = ["Type", "Items", "Title"];
115363
115464
  var otherKeys = setSubtract(Object.keys(section), possibleKeys);
115364
115465
  otherKeys.forEach(function(key) {
@@ -115377,13 +115478,12 @@ function checkBoardYaml(zip, task, report) {
115377
115478
  } else if (item.Type != "card") {
115378
115479
  report.error(`Section items must have Type = "card" but one has Type = "${item.Type}".`);
115379
115480
  } else {
115380
- checkCard2(item);
115481
+ checkCard(item);
115381
115482
  }
115382
115483
  });
115383
115484
  }
115384
115485
  }
115385
115486
  };
115386
- var checkCard = checkCard2, checkSection = checkSection2;
115387
115487
  var cardIds = [];
115388
115488
  if (!Array.isArray(board.Items)) {
115389
115489
  report.error(`The board's Items property is optional, but if it's provided it must be a list of objects.`);
@@ -115397,9 +115497,9 @@ function checkBoardYaml(zip, task, report) {
115397
115497
  report.error(`Every item on the board needs a "Type" property.`);
115398
115498
  } else {
115399
115499
  if (item.Type == "card") {
115400
- checkCard2(item);
115500
+ checkCard(item);
115401
115501
  } else if (item.Type == "section") {
115402
- checkSection2(item);
115502
+ checkSection(item);
115403
115503
  } else {
115404
115504
  report.error(`A board item has an unexpected Type value, we expect either "card" or "section" but got "${item.Type}" instead.`);
115405
115505
  }
@@ -115517,7 +115617,7 @@ function checkCollectionYaml(zip, task, report) {
115517
115617
  }
115518
115618
  try {
115519
115619
  var collection = yaml.load(yamlString);
115520
- var possibleKeys = ["Items", "Tags", "Title", "Version"];
115620
+ var possibleKeys = ["Items", "Tags", "Title", "Version", "FetchExternalImages", "ReuseFolders"];
115521
115621
  var otherKeys = setSubtract(Object.keys(collection), possibleKeys);
115522
115622
  otherKeys.forEach(function(key) {
115523
115623
  report.warn(`Unexpected property "${key}" in collection.yaml.`);
@@ -115534,6 +115634,14 @@ function checkCollectionYaml(zip, task, report) {
115534
115634
  }
115535
115635
  }
115536
115636
  var isV2 = collection.Version == 2;
115637
+ ["FetchExternalImages", "ReuseFolders"].forEach(function(key) {
115638
+ if (collection[key] !== void 0 && collection[key] !== null && typeof collection[key] != "boolean") {
115639
+ report.warn(`The collection's ${key} property must be true or false (${key} = ${escapeHtml3(String(collection[key]))}).`);
115640
+ }
115641
+ });
115642
+ if (collection.ReuseFolders === true && !isV2) {
115643
+ report.warn(`ReuseFolders is ignored unless the collection has "Version: 2" (it applies only to Version 2 non-sync imports).`);
115644
+ }
115537
115645
  if (collection.Items) {
115538
115646
  if (!Array.isArray(collection.Items)) {
115539
115647
  report.error(`The collection's Items list is optional, but if provided it must be a list.`);
@@ -115637,7 +115745,16 @@ function checkResource(zip, task, report) {
115637
115745
  } else {
115638
115746
  report.resources += 1;
115639
115747
  }
115640
- if (!report.resourceReferences.includes(task.filename)) {
115748
+ if (task.filename == "resources/external.json") {
115749
+ var ext = report.externalImages;
115750
+ if (ext) {
115751
+ ext.names.forEach(function(name) {
115752
+ if (!ext.referenced.includes(name) && !report.resourceReferences.includes("resources/" + name)) {
115753
+ report.warn(`resources/external.json entry "${escapeQuotes(name)}" isn't referenced by any cards.`);
115754
+ }
115755
+ });
115756
+ }
115757
+ } else if (!report.resourceReferences.includes(task.filename)) {
115641
115758
  report.warn(`Resource ${task.filename} is included in the zip but isn't referenced by any cards.`);
115642
115759
  }
115643
115760
  if (zip.file(task.filename)._data.uncompressedSize > 2e9) {
@@ -115734,6 +115851,7 @@ function checkZip(zip) {
115734
115851
  collectionRoots: [],
115735
115852
  tags: [],
115736
115853
  resourceReferences: [],
115854
+ externalImages: { enabled: false, names: /* @__PURE__ */ new Set(), referenced: [] },
115737
115855
  cardLinks: 0,
115738
115856
  boardLinks: 0,
115739
115857
  checkCount: 0,
@@ -115807,6 +115925,7 @@ function checkZip(zip) {
115807
115925
  info(`Unexpected file ${fullPath}`);
115808
115926
  }
115809
115927
  }
115928
+ tasks.push({ func: checkExternalImages, context: "resources/external.json", checks: 3, priority: 0 });
115810
115929
  tasks.push({ func: checkFolderTree, checks: 5, priority: 6 });
115811
115930
  tasks.push({ func: checkStats, checks: 3, priority: 10 });
115812
115931
  tasks.sort(function(a, b) {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@getguru/import-engine",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Command-line engine that turns Notion, Confluence, Google, Word, PDF, Markdown and spreadsheet exports into Guru import zips.",
5
5
  "bin": {
6
- "guru-import-engine": "./docx-import-cli.js"
6
+ "guru-import-engine": "docx-import-cli.js"
7
7
  },
8
8
  "files": [
9
9
  "docx-import-cli.js",
@@ -11,10 +11,10 @@
11
11
  "LICENSE.md"
12
12
  ],
13
13
  "dependencies": {
14
- "jsdom": "26.1.0"
14
+ "jsdom": "28.1.0"
15
15
  },
16
16
  "engines": {
17
- "node": ">=22"
17
+ "node": ">=22.12"
18
18
  },
19
19
  "publishConfig": {
20
20
  "access": "public"