@islamihab/kds 0.1.1 → 0.1.3

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.
Files changed (2) hide show
  1. package/index.js +281 -39
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -14404,8 +14404,16 @@ var printHelp = (help) => {
14404
14404
  description: `${field.description ?? ""}${isRequired(field) ? " (required)" : ""}`
14405
14405
  }));
14406
14406
  const usagePositionals = help.positionals?.map(([key, field]) => isRequired(field) ? ` <${key}>` : ` [${key}]`).join("") ?? "";
14407
- const options = help.options?.filter(({ field }) => !isHidden(field)).concat([{ key: "help", field: exports_external.boolean().default(false).describe("Show this help"), short: "h" }]).map(({ key, field, short }) => ({
14408
- label: `${short ? `-${short}, ` : " "}--${key}${valuePlaceholder(field)}`,
14407
+ const options = help.options?.filter(({ field }) => !isHidden(field)).concat([
14408
+ {
14409
+ key: "help",
14410
+ field: exports_external.boolean().default(false).describe("Show this help"),
14411
+ short: "h",
14412
+ negatedKey: undefined,
14413
+ negatedValue: undefined
14414
+ }
14415
+ ]).map(({ key, field, short, negatedKey }) => ({
14416
+ label: `${short ? `-${short}, ` : " "}--${key}${valuePlaceholder(field)}${negatedKey ? ` | --${negatedKey}` : ""}`,
14409
14417
  description: `${field.description ?? ""}${isRequired(field) ? " (required)" : ""}`
14410
14418
  }));
14411
14419
  const usageOptions = options?.length ? " [options]" : "";
@@ -14413,7 +14421,9 @@ var printHelp = (help) => {
14413
14421
  console.log(`${help.description}
14414
14422
  `);
14415
14423
  console.log(`Usage: ${help.path}${usageCommands}${usageOptions}${usagePositionals}`);
14416
- const printSection = (title, entries, width) => {
14424
+ const labels = [commands, positionals, options].flatMap((entries) => entries ?? []);
14425
+ const width = Math.max(...labels.map(({ label }) => label.length));
14426
+ const printSection = (title, entries) => {
14417
14427
  if (!entries?.length)
14418
14428
  return;
14419
14429
  console.log(`
@@ -14421,13 +14431,13 @@ ${title}:`);
14421
14431
  for (const entry of entries)
14422
14432
  console.log(` ${entry.label.padEnd(width)} ${entry.description}`);
14423
14433
  };
14424
- printSection("Commands", commands, 12);
14425
- printSection("Arguments", positionals, 28);
14426
- printSection("Options", options, 28);
14434
+ printSection("Commands", commands);
14435
+ printSection("Arguments", positionals);
14436
+ printSection("Options", options);
14427
14437
  };
14428
14438
  var baseType = (field) => {
14429
14439
  let t = field;
14430
- while (t instanceof exports_external.ZodOptional || t instanceof exports_external.ZodDefault) {
14440
+ while (t instanceof exports_external.ZodOptional || t instanceof exports_external.ZodDefault || t instanceof exports_external.ZodNullable) {
14431
14441
  const inner = t.unwrap();
14432
14442
  if (!(inner instanceof exports_external.ZodType))
14433
14443
  return t;
@@ -14437,6 +14447,7 @@ var baseType = (field) => {
14437
14447
  };
14438
14448
  var isRequired = (field) => !(field instanceof exports_external.ZodOptional || field instanceof exports_external.ZodDefault);
14439
14449
  var isHidden = (field) => field.meta()?.hidden === true;
14450
+ var isNegatable = (field) => field.meta()?.negatable === true;
14440
14451
  var isHelpFlag = (arg) => arg === "-h" || arg === "--help";
14441
14452
  var valuePlaceholder = (field) => {
14442
14453
  const base = baseType(field);
@@ -14462,16 +14473,34 @@ var shortFlag = (key, field, usedShortFlags) => {
14462
14473
  usedShortFlags.add(short);
14463
14474
  return short;
14464
14475
  };
14476
+ var optionEntries = (shape) => {
14477
+ const usedShortFlags = new Set;
14478
+ const optionKeys = new Set(Object.keys(shape));
14479
+ return Object.entries(shape).map(([key, field]) => {
14480
+ const negatedKey = isNegatable(field) ? `no-${key}` : undefined;
14481
+ const negatedValue = negatedKey && baseType(field) instanceof exports_external.ZodBoolean ? false : negatedKey ? null : undefined;
14482
+ if (negatedKey && !field.safeParse(negatedValue).success) {
14483
+ throw new Error(`Option '--${key}' is negatable, but its schema does not accept ${String(negatedValue)}`);
14484
+ }
14485
+ if (negatedKey && optionKeys.has(negatedKey)) {
14486
+ throw new Error(`Option '--${negatedKey}' conflicts with the negated form of '--${key}'`);
14487
+ }
14488
+ return { key, field, short: shortFlag(key, field, usedShortFlags), negatedKey, negatedValue };
14489
+ });
14490
+ };
14465
14491
  var parseArgs = ({
14466
14492
  args,
14467
14493
  positionalEntries,
14468
14494
  optionsEntries,
14469
14495
  help
14470
14496
  }) => {
14471
- const options = Object.fromEntries(optionsEntries.map(({ key, field, short }) => {
14497
+ const options = {};
14498
+ for (const { key, field, short, negatedKey } of optionsEntries) {
14472
14499
  const type = baseType(field) instanceof exports_external.ZodBoolean ? "boolean" : "string";
14473
- return [key, short ? { short, type } : { type }];
14474
- }));
14500
+ options[key] = short ? { short, type } : { type };
14501
+ if (negatedKey)
14502
+ options[negatedKey] = { type: "boolean" };
14503
+ }
14475
14504
  try {
14476
14505
  const { values, positionals } = nodeParseArgs({
14477
14506
  args,
@@ -14482,7 +14511,18 @@ var parseArgs = ({
14482
14511
  help();
14483
14512
  throw new Error(`Unexpected argument '${positionals[positionalEntries.length]}'`);
14484
14513
  }
14485
- return { values, positionals };
14514
+ const normalizedValues = { ...values };
14515
+ for (const { key, negatedKey, negatedValue } of optionsEntries) {
14516
+ if (!negatedKey || !Object.hasOwn(normalizedValues, negatedKey))
14517
+ continue;
14518
+ if (Object.hasOwn(normalizedValues, key)) {
14519
+ help();
14520
+ throw new Error(`Options '--${key}' and '--${negatedKey}' cannot be used together`);
14521
+ }
14522
+ delete normalizedValues[negatedKey];
14523
+ normalizedValues[key] = negatedValue;
14524
+ }
14525
+ return { values: normalizedValues, positionals };
14486
14526
  } catch (error51) {
14487
14527
  if (error51 instanceof Error && "code" in error51) {
14488
14528
  if (error51.code === "ERR_PARSE_ARGS_UNKNOWN_OPTION") {
@@ -14500,12 +14540,7 @@ var parseArgs = ({
14500
14540
  var command = (def) => {
14501
14541
  const inputShape = exports_external.object({ positionals: exports_external.object(def.positionals), options: exports_external.object(def.options) });
14502
14542
  const positionalEntries = Object.entries(inputShape.shape.positionals.shape);
14503
- const usedShortFlags = new Set;
14504
- const optionsEntries = Object.entries(inputShape.shape.options.shape).map(([key, field]) => ({
14505
- key,
14506
- field,
14507
- short: shortFlag(key, field, usedShortFlags)
14508
- }));
14543
+ const optionsEntries = optionEntries(inputShape.shape.options.shape);
14509
14544
  const run = async (args, path) => {
14510
14545
  const help = () => printHelp({ path, description: def.description, positionals: positionalEntries, options: optionsEntries });
14511
14546
  if (args.some(isHelpFlag))
@@ -14523,12 +14558,7 @@ var command = (def) => {
14523
14558
  };
14524
14559
  var group = (def) => {
14525
14560
  const optionsShape = exports_external.object(def.options);
14526
- const usedShortFlags = new Set;
14527
- const optionsEntries = Object.entries(optionsShape.shape).map(([key, field]) => ({
14528
- key,
14529
- field,
14530
- short: shortFlag(key, field, usedShortFlags)
14531
- }));
14561
+ const optionsEntries = optionEntries(optionsShape.shape);
14532
14562
  const run = async ([sub, ...rest], path) => {
14533
14563
  const help = () => printHelp({ path, description: def.description, commands: def.commands, options: optionsEntries });
14534
14564
  if (!sub && !def.run || sub === "help" || isHelpFlag(sub))
@@ -14557,7 +14587,7 @@ import { join } from "path";
14557
14587
  // package.json
14558
14588
  var package_default = {
14559
14589
  name: "cli",
14560
- version: "0.1.1",
14590
+ version: "0.1.3",
14561
14591
  private: true,
14562
14592
  type: "module",
14563
14593
  bin: {
@@ -15081,6 +15111,9 @@ var components = componentsGeneric();
15081
15111
 
15082
15112
  // ../../packages/backend/convex/constants.ts
15083
15113
  var KDS_DEVICE_AUTH_CLIENT_ID = "kds-cli";
15114
+ var PAGE_MODES = ["themed", "raw"];
15115
+ var PAGE_VISIBILITIES = ["public", "private"];
15116
+ var MAX_PAGE_HTML_BYTES = 4000000;
15084
15117
 
15085
15118
  // src/commands/auth/login.ts
15086
15119
  import open from "open";
@@ -17651,7 +17684,7 @@ var login = command({
17651
17684
  description: "Sign in securely through your browser",
17652
17685
  options: {
17653
17686
  server: exports_external.url().optional().describe("Override KDS server URL (only used in development)").meta({ hidden: true }),
17654
- "no-open": exports_external.boolean().optional().default(false).describe("Do not open the browser automatically")
17687
+ open: exports_external.boolean().optional().default(true).describe("Open the browser automatically").meta({ negatable: true })
17655
17688
  },
17656
17689
  run: async ({ options }) => {
17657
17690
  const oldConfig = await loadConfig({ throwIfMissing: false, throwIfInvalid: false });
@@ -17673,7 +17706,7 @@ Authorize this CLI in your browser:`);
17673
17706
  console.log(` ${browserUrl}`);
17674
17707
  console.log(`
17675
17708
  Device code: ${userCode}`);
17676
- if (!options["no-open"]) {
17709
+ if (options.open) {
17677
17710
  if (await openBrowser(browserUrl))
17678
17711
  console.log(`
17679
17712
  Opened your browser.`);
@@ -17723,25 +17756,234 @@ var auth = group({
17723
17756
  commands: [login, logout, status]
17724
17757
  });
17725
17758
 
17726
- // src/commands/hello.ts
17727
- var hello = command({
17728
- name: "hello",
17729
- description: "Example command \u2014 replace with a real tool",
17730
- hidden: true,
17759
+ // src/lib/group.ts
17760
+ import { basename } from "path";
17761
+ var git = async (...args) => {
17762
+ const proc = Bun.spawn(["git", ...args], { stdout: "pipe", stderr: "ignore" });
17763
+ const [output, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
17764
+ return exitCode === 0 ? output.trim() || null : null;
17765
+ };
17766
+ var repoNameFromRemote = (remote) => remote.replace(/\/+$/, "").replace(/\.git$/, "").split(/[/:]/).pop() || undefined;
17767
+ var detectRepoGroup = async () => {
17768
+ const remote = await git("remote", "get-url", "origin");
17769
+ if (remote)
17770
+ return repoNameFromRemote(remote);
17771
+ const root = await git("rev-parse", "--show-toplevel");
17772
+ return root ? basename(root) : undefined;
17773
+ };
17774
+ var groupForCreate = async (group2) => group2 === null ? undefined : group2 ?? await detectRepoGroup();
17775
+
17776
+ // src/lib/input.ts
17777
+ var assertSize = (size) => {
17778
+ if (size > MAX_PAGE_HTML_BYTES)
17779
+ throw new Error(`Page is too large (${size} bytes; max ${MAX_PAGE_HTML_BYTES}).`);
17780
+ };
17781
+ var readBody = async (path) => {
17782
+ if (path === "-") {
17783
+ const html = await Bun.stdin.text();
17784
+ assertSize(new TextEncoder().encode(html).length);
17785
+ return html;
17786
+ }
17787
+ const file2 = Bun.file(path);
17788
+ assertSize(file2.size);
17789
+ return await file2.text();
17790
+ };
17791
+ var readPageId = (value) => {
17792
+ const trimmed = value.trim();
17793
+ try {
17794
+ const url2 = new URL(trimmed);
17795
+ const pagePath = url2.pathname.match(/\/p\/([^/]+)\/?$/);
17796
+ return pagePath?.[1] ?? trimmed;
17797
+ } catch {
17798
+ return trimmed;
17799
+ }
17800
+ };
17801
+
17802
+ // src/commands/pages/create.ts
17803
+ var create = command({
17804
+ name: "create",
17805
+ description: "Publish a page and print its URL",
17806
+ positionals: {
17807
+ file: exports_external.string().describe("HTML file, or - for stdin")
17808
+ },
17809
+ options: {
17810
+ title: exports_external.string().describe("Page title").meta({ short: "t" }),
17811
+ group: exports_external.string().nullable().optional().describe("Group to list the page under (default: the current repo)").meta({ short: "g", negatable: true }),
17812
+ raw: exports_external.boolean().default(false).describe("Serve a full document verbatim (may run JS)"),
17813
+ private: exports_external.boolean().default(false).describe("Require the publishing account to view the page")
17814
+ },
17815
+ run: async ({ positionals: { file: file2 }, options: { title, group: group2, raw, private: isPrivate } }) => {
17816
+ const newGroup = await groupForCreate(group2);
17817
+ const html = await readBody(file2);
17818
+ const mode = raw ? "raw" : "themed";
17819
+ const visibility = isPrivate ? "private" : "public";
17820
+ const { url: url2 } = await (await backendClient()).action(api2.pages.create, {
17821
+ title,
17822
+ group: newGroup,
17823
+ html,
17824
+ mode,
17825
+ visibility
17826
+ });
17827
+ console.log(url2);
17828
+ }
17829
+ });
17830
+
17831
+ // src/commands/pages/get.ts
17832
+ var get = command({
17833
+ name: "get",
17834
+ description: "Print a page's HTML",
17731
17835
  positionals: {
17732
- name: exports_external.string().default("world").describe("Who to greet")
17836
+ id: exports_external.string().describe("Page id or URL")
17733
17837
  },
17734
17838
  options: {
17735
- shout: exports_external.boolean().default(false).describe("Greet loudly").meta({ short: "s" }),
17736
- repeat: exports_external.coerce.number().int().positive().default(1).describe("Number of times to greet").meta({ short: "r" })
17839
+ json: exports_external.boolean().default(false).describe("Print the full page as JSON"),
17840
+ version: exports_external.coerce.number().int().positive().optional().describe("Print a previous version instead of the current")
17737
17841
  },
17738
- run: ({ positionals: { name }, options: { shout, repeat } }) => {
17739
- const greeting = `Hello, ${name}!`;
17740
- for (let i2 = 0;i2 < repeat; i2++)
17741
- console.log(shout ? greeting.toUpperCase() : greeting);
17842
+ run: async ({ positionals: { id }, options: { json: json2, version: version4 } }) => {
17843
+ const page = await (await backendClient()).action(api2.pages.get, { id: readPageId(id), version: version4 });
17844
+ if (json2)
17845
+ console.log(JSON.stringify(page, null, 2));
17846
+ else
17847
+ process.stdout.write(page.html);
17742
17848
  }
17743
17849
  });
17744
17850
 
17851
+ // ../../packages/backend/convex/lib/format.ts
17852
+ var formatBytes = (bytes) => bytes < 1024 ? `${bytes} B` : `${Math.round(bytes / 1024)} KB`;
17853
+
17854
+ // src/lib/output.ts
17855
+ var printTable = (rows) => {
17856
+ if (rows.length === 0)
17857
+ return;
17858
+ const columnCount = Math.max(...rows.map((row) => row.length));
17859
+ const widths = Array.from({ length: columnCount }, (_, column) => Math.max(...rows.map((row) => (row[column] ?? "").length)));
17860
+ for (const row of rows) {
17861
+ console.log(row.map((cell, column) => cell.padEnd(widths[column] ?? 0)).join(" ").trimEnd());
17862
+ }
17863
+ };
17864
+
17865
+ // src/commands/pages/list.ts
17866
+ var list = command({
17867
+ name: "list",
17868
+ description: "List your published pages",
17869
+ options: {
17870
+ group: exports_external.string().nullable().optional().describe("Only list pages in this group, or --no-group for the ungrouped ones").meta({ short: "g", negatable: true }),
17871
+ json: exports_external.boolean().default(false).describe("Print as JSON")
17872
+ },
17873
+ run: async ({ options: { group: group2, json: json2 } }) => {
17874
+ const pages = await (await backendClient()).query(api2.pages.list, { group: group2 });
17875
+ if (json2)
17876
+ return console.log(JSON.stringify(pages, null, 2));
17877
+ if (pages.length === 0)
17878
+ return console.log(group2 === undefined ? "No pages yet." : "No pages in that group.");
17879
+ printTable([
17880
+ ["TITLE", "GROUP", "ACCESS", "MODE", "SIZE", "VERSIONS", "CREATED", "URL"],
17881
+ ...pages.map((page) => [
17882
+ page.title,
17883
+ page.group ?? "-",
17884
+ page.visibility,
17885
+ page.mode,
17886
+ formatBytes(page.size),
17887
+ page.versions === 0 ? "-" : String(page.versions),
17888
+ new Date(page.createdAt).toISOString().slice(0, 10),
17889
+ page.url
17890
+ ])
17891
+ ]);
17892
+ }
17893
+ });
17894
+
17895
+ // src/commands/pages/remove.ts
17896
+ var remove = command({
17897
+ name: "delete",
17898
+ description: "Delete a page",
17899
+ positionals: {
17900
+ id: exports_external.string().describe("Page id or URL")
17901
+ },
17902
+ run: async ({ positionals: { id } }) => {
17903
+ await (await backendClient()).mutation(api2.pages.remove, { id: readPageId(id) });
17904
+ console.log("Deleted.");
17905
+ }
17906
+ });
17907
+
17908
+ // src/commands/pages/revert.ts
17909
+ var revert = command({
17910
+ name: "revert",
17911
+ description: "Restore a previous version as the page's content",
17912
+ positionals: {
17913
+ id: exports_external.string().describe("Page id or URL"),
17914
+ version: exports_external.coerce.number().int().positive().describe("Version number from `kds pages versions`")
17915
+ },
17916
+ run: async ({ positionals: { id, version: version4 } }) => {
17917
+ const { url: url2 } = await (await backendClient()).mutation(api2.pages.revert, { id: readPageId(id), version: version4 });
17918
+ console.log(url2);
17919
+ }
17920
+ });
17921
+
17922
+ // src/commands/pages/update.ts
17923
+ var update = command({
17924
+ name: "update",
17925
+ description: "Replace a page's HTML, title, group, mode, or visibility",
17926
+ positionals: {
17927
+ id: exports_external.string().describe("Page id or URL"),
17928
+ file: exports_external.string().optional().describe("HTML file, or - for stdin")
17929
+ },
17930
+ options: {
17931
+ title: exports_external.string().optional().describe("New page title").meta({ short: "t" }),
17932
+ group: exports_external.string().nullable().optional().describe("Move the page to this group, or --no-group to remove it from one").meta({ short: "g", negatable: true }),
17933
+ mode: exports_external.enum(PAGE_MODES).optional().describe("New serving mode"),
17934
+ visibility: exports_external.enum(PAGE_VISIBILITIES).optional().describe("Who can view the page")
17935
+ },
17936
+ run: async ({ positionals: { id, file: file2 }, options: { title, group: group2, mode, visibility } }) => {
17937
+ if (!file2 && title === undefined && group2 === undefined && mode === undefined && visibility === undefined)
17938
+ throw new Error("Nothing to update. Pass a file, --title, --group, --no-group, --mode, or --visibility.");
17939
+ const html = file2 ? await readBody(file2) : undefined;
17940
+ const { url: url2 } = await (await backendClient()).action(api2.pages.update, {
17941
+ id: readPageId(id),
17942
+ html,
17943
+ title,
17944
+ group: group2,
17945
+ mode,
17946
+ visibility
17947
+ });
17948
+ console.log(url2);
17949
+ }
17950
+ });
17951
+
17952
+ // src/commands/pages/versions.ts
17953
+ var versions2 = command({
17954
+ name: "versions",
17955
+ description: "List a page's previous versions",
17956
+ positionals: {
17957
+ id: exports_external.string().describe("Page id or URL")
17958
+ },
17959
+ options: {
17960
+ json: exports_external.boolean().default(false).describe("Print as JSON")
17961
+ },
17962
+ run: async ({ positionals: { id }, options: { json: json2 } }) => {
17963
+ const archived = await (await backendClient()).query(api2.pages.versions, { id: readPageId(id) });
17964
+ if (json2)
17965
+ return console.log(JSON.stringify(archived, null, 2));
17966
+ if (archived.length === 0)
17967
+ return console.log("No previous versions.");
17968
+ printTable([
17969
+ ["VERSION", "MODE", "SIZE", "ARCHIVED"],
17970
+ ...archived.map((version4) => [
17971
+ String(version4.version),
17972
+ version4.mode,
17973
+ formatBytes(version4.size),
17974
+ new Date(version4.archivedAt).toISOString().slice(0, 10)
17975
+ ])
17976
+ ]);
17977
+ }
17978
+ });
17979
+
17980
+ // src/commands/pages/index.ts
17981
+ var pages = group({
17982
+ name: "pages",
17983
+ description: "Publish HTML documents to the web",
17984
+ commands: [create, list, get, update, versions2, revert, remove]
17985
+ });
17986
+
17745
17987
  // src/commands/upgrade.ts
17746
17988
  var upgrade = command({
17747
17989
  name: "upgrade",
@@ -17757,7 +17999,7 @@ var upgrade = command({
17757
17999
  var rootCommand = group({
17758
18000
  name: "kds",
17759
18001
  description: "KDS CLI",
17760
- commands: [auth, hello, upgrade],
18002
+ commands: [auth, pages, upgrade],
17761
18003
  options: {
17762
18004
  version: exports_external.boolean().default(false).describe("Show the version").meta({ short: "v" })
17763
18005
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@islamihab/kds",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Command-line client for Kai Dev Studio",
5
5
  "license": "MIT",
6
6
  "publishConfig": {