@puzzmo/sdk 1.0.16 → 1.0.18

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/dist/vite.js CHANGED
@@ -1,19 +1,9 @@
1
1
  import { t as e } from "./asyncToGenerator-CPSNHDFw.js";
2
2
  import { t } from "./objectSpread2-vLYiAtaU.js";
3
3
  import { build as n } from "vite";
4
- var r = Object.create, i = Object.defineProperty, a = Object.getOwnPropertyDescriptor, o = Object.getOwnPropertyNames, s = Object.getPrototypeOf, c = Object.prototype.hasOwnProperty, l = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports), u = (e, t, n, r) => {
5
- if (t && typeof t == "object" || typeof t == "function") for (var s = o(t), l = 0, u = s.length, d; l < u; l++) d = s[l], !c.call(e, d) && d !== n && i(e, d, {
6
- get: ((e) => t[e]).bind(null, d),
7
- enumerable: !(r = a(t, d)) || r.enumerable
8
- });
9
- return e;
10
- }, d = /* @__PURE__ */ ((e, t, n) => (n = e == null ? {} : r(s(e)), u(t || !e || !e.__esModule ? i(n, "default", {
11
- value: e,
12
- enumerable: !0
13
- }) : n, e)))((/* @__PURE__ */ l(((e, t) => {
14
- t.exports = {};
15
- })))(), 1);
16
- function f(e, t) {
4
+ import r from "path";
5
+ import i from "fs";
6
+ function a(e, t) {
17
7
  if (e == null) return {};
18
8
  var n = {};
19
9
  for (var r in e) if ({}.hasOwnProperty.call(e, r)) {
@@ -22,46 +12,95 @@ function f(e, t) {
22
12
  }
23
13
  return n;
24
14
  }
25
- function p(e, t) {
15
+ function o(e, t) {
26
16
  if (e == null) return {};
27
- var n, r, i = f(e, t);
17
+ var n, r, i = a(e, t);
28
18
  if (Object.getOwnPropertySymbols) {
29
- var a = Object.getOwnPropertySymbols(e);
30
- for (r = 0; r < a.length; r++) n = a[r], t.includes(n) || {}.propertyIsEnumerable.call(e, n) && (i[n] = e[n]);
19
+ var o = Object.getOwnPropertySymbols(e);
20
+ for (r = 0; r < o.length; r++) n = o[r], t.includes(n) || {}.propertyIsEnumerable.call(e, n) && (i[n] = e[n]);
31
21
  }
32
22
  return i;
33
23
  }
34
- var m = ["fixturesGlob"], h = "/@puzzmo-simulator-init.js", g = "virtual:puzzmo-simulator", _ = "\0" + g;
24
+ var s = ["fixturesGlob"], c = "/@puzzmo-simulator-init.js", l = "virtual:puzzmo-simulator";
25
+ /**
26
+ * Discover all games from puzzmo.json files under a root directory.
27
+ * @internal
28
+ */
29
+ function u(e) {
30
+ let t = /* @__PURE__ */ new Map(), n = _(e, 3);
31
+ i.existsSync(r.join(e, "puzzmo.json")) && n.unshift(e);
32
+ for (let o of n) try {
33
+ var a;
34
+ let n = JSON.parse(i.readFileSync(r.join(o, "puzzmo.json"), "utf-8"));
35
+ if (!(!(n == null || (a = n.game) == null) && a.slug)) continue;
36
+ let s = r.join(o, "src", "appBundle.js"), c = null;
37
+ i.existsSync(s) && (c = "/" + r.relative(e, s).split(r.sep).join("/")), t.set(n.game.slug, {
38
+ dir: o,
39
+ slug: n.game.slug,
40
+ displayName: n.game.displayName,
41
+ appBundlePath: c
42
+ });
43
+ } catch (e) {}
44
+ return t;
45
+ }
46
+ /**
47
+ * Resolve which game a request belongs to using the referer URL.
48
+ * @internal
49
+ */
50
+ function d(e, t, n) {
51
+ if (e) try {
52
+ let i = new URL(e).pathname;
53
+ for (let e of t.values()) {
54
+ let t = "/" + r.relative(n, e.dir).split(r.sep).join("/");
55
+ if (i.startsWith(t + "/") || i === t) return e;
56
+ }
57
+ } catch (e) {}
58
+ if (t.size === 1) return t.values().next().value;
59
+ }
60
+ /**
61
+ * Generate the virtual module code for the simulator.
62
+ * @internal
63
+ */
64
+ function f(e, n) {
65
+ let { fixturesGlob: r } = e, i = o(e, s), a = r === !1 ? null : r == null ? "/fixtures/puzzles/**/*.json" : r, c = ["import { createSimulator } from \"@puzzmo/sdk/simulator\""];
66
+ a && c.push(`const fixtures = import.meta.glob(${JSON.stringify(a)}, { eager: true })`), n != null && n.appBundlePath && (c.push(`import(${JSON.stringify(n.appBundlePath)}).then(m => {`), c.push(" if (m.renderThumbnail) globalThis.renderThumbnail = m.renderThumbnail"), c.push("}).catch(() => {})"));
67
+ let l = t(t({}, i), n != null && n.slug ? { slug: n.slug } : {}), u = Object.entries(l).filter(([, e]) => e !== void 0).map(([e, t]) => `${e}: ${JSON.stringify(t)}`);
68
+ return a && u.push("fixtures"), c.push(`createSimulator({ ${u.join(", ")} })`), c.join("\n");
69
+ }
35
70
  /** Vite plugin that injects the Puzzmo simulator in dev mode and handles OAuth callbacks. */
36
- function v(t = {}) {
37
- function n() {
38
- let { fixturesGlob: e } = t, n = p(t, m), r = ["import { createSimulator } from \"@puzzmo/sdk/simulator\""];
39
- e && r.push(`const fixtures = import.meta.glob(${JSON.stringify(e)}, { eager: true })`);
40
- let i = Object.entries(n).filter(([, e]) => e !== void 0).map(([e, t]) => `${e}: ${JSON.stringify(t)}`);
41
- return e && i.push("fixtures"), r.push(`createSimulator({ ${i.join(", ")} })`), r.join("\n");
42
- }
71
+ function p(t = {}) {
72
+ let n = /* @__PURE__ */ new Map(), r;
43
73
  return {
44
74
  name: "puzzmo-simulator",
45
75
  apply: "serve",
76
+ configResolved(e) {
77
+ if (r = e.root, n = u(r), n.size === 0) e.logger.info("\x1B[33m\x1B[1m PUZZMO \x1B[22m\x1B[39m\x1B[2m no puzzmo.json files found\x1B[22m");
78
+ else {
79
+ let t = [...n.values()].map((e) => `\x1b[36m${e.slug}\x1b[39m`), r = n.size === 1 ? "game" : "games";
80
+ e.logger.info(`\x1b[33m\x1b[1m PUZZMO \x1b[22m\x1b[39m found ${n.size} ${r}: ${t.join("\x1B[2m, \x1B[22m")}`);
81
+ }
82
+ },
46
83
  resolveId(e) {
47
- if (e === g) return _;
84
+ if (e === l || e.startsWith(l + "?")) return "\0" + e;
48
85
  },
49
86
  load(e) {
50
- if (e === _) return n();
87
+ if (!e.startsWith("\0" + l)) return;
88
+ let r = new URLSearchParams(e.split("?")[1] || "").get("game");
89
+ return f(t, r ? n.get(r) : n.size === 1 ? n.values().next().value : void 0);
51
90
  },
52
91
  configureServer(t) {
53
92
  t.middlewares.use("/oauth/callback", (e, t) => {
54
93
  t.setHeader("Content-Type", "text/html"), t.end("<!DOCTYPE html>\n<html><head><title>Puzzmo OAuth</title></head>\n<body><script>\nvar params = new URLSearchParams(window.location.search);\nvar returnUrl = sessionStorage.getItem(\"oauth_return_url\") || \"/\";\nvar url = new URL(returnUrl);\nparams.forEach(function(v, k) { url.searchParams.set(k, v); });\nwindow.location.href = url.toString();\n<\/script></body></html>");
55
94
  }), t.middlewares.use(function() {
56
- var n = e(function* (e, n, r) {
57
- var i;
58
- if (((i = e.url) == null ? void 0 : i.split("?")[0]) !== h) return r();
59
- let a = yield t.transformRequest(g);
60
- if (!a) return r();
61
- n.setHeader("Content-Type", "application/javascript"), n.end(a.code);
95
+ var i = e(function* (e, i, a) {
96
+ var o;
97
+ if (((o = e.url) == null ? void 0 : o.split("?")[0]) !== c) return a();
98
+ let s = d(e.headers.referer, n, r), u = l + (s ? `?game=${s.slug}` : ""), f = yield t.transformRequest(u);
99
+ if (!f) return a();
100
+ i.setHeader("Content-Type", "application/javascript"), i.end(f.code);
62
101
  });
63
- return function(e, t, r) {
64
- return n.apply(this, arguments);
102
+ return function(e, t, n) {
103
+ return i.apply(this, arguments);
65
104
  };
66
105
  }());
67
106
  },
@@ -70,18 +109,18 @@ function v(t = {}) {
70
109
  tag: "script",
71
110
  attrs: {
72
111
  type: "module",
73
- src: h
112
+ src: c
74
113
  },
75
114
  injectTo: "head"
76
115
  }];
77
116
  }
78
117
  };
79
118
  }
80
- function y(r, i) {
81
- return (a = {}) => {
82
- let { entry: o, outputFile: s } = t(t({}, i), a);
119
+ function m(i, a) {
120
+ return (o = {}) => {
121
+ let { entry: s, outputFile: c } = t(t({}, a), o);
83
122
  return {
84
- name: r,
123
+ name: i,
85
124
  apply: "build",
86
125
  closeBundle() {
87
126
  return e(function* () {
@@ -91,16 +130,16 @@ function y(r, i) {
91
130
  logLevel: "warn",
92
131
  build: {
93
132
  lib: {
94
- entry: d.default.isAbsolute(o) ? o : d.default.resolve(process.cwd(), o),
133
+ entry: r.isAbsolute(s) ? s : r.resolve(process.cwd(), s),
95
134
  formats: ["es"],
96
- fileName: () => s
135
+ fileName: () => c
97
136
  },
98
137
  outDir: "dist",
99
138
  emptyOutDir: !1
100
139
  }
101
140
  });
102
141
  } catch (e) {
103
- throw console.error(`[${r}] build failed:`, e), e;
142
+ throw console.error(`[${i}] build failed:`, e), e;
104
143
  }
105
144
  })();
106
145
  }
@@ -113,13 +152,27 @@ function y(r, i) {
113
152
  * The bundle exports `renderThumbnail(puzzleStr, inputStr?, config?)` — a pure
114
153
  * SVG-string renderer used by the Puzzmo platform for puzzle previews.
115
154
  */
116
- const b = y("app-bundle", {
155
+ const h = m("app-bundle", {
117
156
  entry: "src/appBundle.js",
118
157
  outputFile: "app-bundle.js"
119
- }), x = y("editor-bundle", {
158
+ }), g = m("editor-bundle", {
120
159
  entry: "src/editorBundle.js",
121
160
  outputFile: "editor-bundle.js"
122
- });
123
- export { b as appBundlePlugin, x as editorBundlePlugin, v as puzzmoSimulator };
161
+ }), _ = (e, t, n = 0) => {
162
+ if (n >= t) return [];
163
+ let a = [], o;
164
+ try {
165
+ o = i.readdirSync(e, { withFileTypes: !0 });
166
+ } catch (e) {
167
+ return a;
168
+ }
169
+ for (let s of o) {
170
+ if (!s.isDirectory() || s.name.startsWith(".") || s.name === "node_modules") continue;
171
+ let o = r.join(e, s.name);
172
+ i.existsSync(r.join(o, "puzzmo.json")) && a.push(o), a.push(..._(o, t, n + 1));
173
+ }
174
+ return a;
175
+ };
176
+ export { h as appBundlePlugin, u as discoverGames, g as editorBundlePlugin, _ as findPuzzmoJsonDirs, f as generateSimulatorCode, p as puzzmoSimulator, d as resolveGameFromReferer };
124
177
 
125
178
  //# sourceMappingURL=vite.js.map
package/dist/vite.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vite.js","names":[],"sources":["../__vite-browser-external","../src/vite.ts"],"sourcesContent":["module.exports = {}","import type { Plugin } from \"vite\"\nimport { build } from \"vite\"\nimport path from \"path\"\n\nexport type PuzzmoSimulatorPluginOptions = {\n /** Whether to auto-start the game after READY (default: true) */\n autoStart?: boolean\n /** Initial collapsed state (default: true) */\n collapsed?: boolean\n /** Game slug for API features (e.g. \"crossword\", \"my-game\") */\n slug?: string\n /** Glob pattern for fixture files, passed to import.meta.glob (e.g. \"/fixtures/puzzles/**\\/*.json\") */\n fixturesGlob?: string\n}\n\nconst SIMULATOR_URL = \"/@puzzmo-simulator-init.js\"\nconst VIRTUAL_ID = \"virtual:puzzmo-simulator\"\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ID\n\n/** Vite plugin that injects the Puzzmo simulator in dev mode and handles OAuth callbacks. */\nexport function puzzmoSimulator(options: PuzzmoSimulatorPluginOptions = {}): Plugin {\n function generateCode(): string {\n const { fixturesGlob, ...config } = options\n\n const lines = [`import { createSimulator } from \"@puzzmo/sdk/simulator\"`]\n\n if (fixturesGlob) {\n lines.push(`const fixtures = import.meta.glob(${JSON.stringify(fixturesGlob)}, { eager: true })`)\n }\n\n const configEntries = Object.entries(config).filter(([, v]) => v !== undefined)\n const configParts = configEntries.map(([k, v]) => `${k}: ${JSON.stringify(v)}`)\n if (fixturesGlob) configParts.push(\"fixtures\")\n\n lines.push(`createSimulator({ ${configParts.join(\", \")} })`)\n\n return lines.join(\"\\n\")\n }\n\n return {\n name: \"puzzmo-simulator\",\n apply: \"serve\",\n\n // Virtual module used internally for code generation and transformation.\n // Uses \\0 prefix so Vite's HTML processor won't try to inline it.\n resolveId(id) {\n if (id === VIRTUAL_ID) return RESOLVED_VIRTUAL_ID\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) return generateCode()\n },\n\n configureServer(server) {\n server.middlewares.use(\"/oauth/callback\", (_req, res) => {\n res.setHeader(\"Content-Type\", \"text/html\")\n res.end(`<!DOCTYPE html>\n<html><head><title>Puzzmo OAuth</title></head>\n<body><script>\nvar params = new URLSearchParams(window.location.search);\nvar returnUrl = sessionStorage.getItem(\"oauth_return_url\") || \"/\";\nvar url = new URL(returnUrl);\nparams.forEach(function(v, k) { url.searchParams.set(k, v); });\nwindow.location.href = url.toString();\n</script></body></html>`)\n })\n\n // Serve the simulator init module. Registered before Vite's internal\n // middleware so the SPA fallback doesn't intercept it.\n server.middlewares.use(async (req, res, next) => {\n if (req.url?.split(\"?\")[0] !== SIMULATOR_URL) return next()\n\n const result = await server.transformRequest(VIRTUAL_ID)\n if (!result) return next()\n res.setHeader(\"Content-Type\", \"application/javascript\")\n res.end(result.code)\n })\n },\n\n // Inject a script tag whose src the browser will fetch.\n // The URL is NOT resolvable via resolveId (intentionally), so Vite's\n // HTML processor won't inline it. The middleware above serves it instead.\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: { type: \"module\", src: SIMULATOR_URL },\n injectTo: \"head\",\n },\n ]\n },\n }\n}\n\ntype BundlePluginOptions = {\n /** Entry file for the bundle */\n entry: string\n /** Output file name */\n outputFile: string\n}\n\nfunction createBundlePlugin(pluginName: string, defaults: BundlePluginOptions) {\n return (options: Partial<BundlePluginOptions> = {}): Plugin => {\n const { entry, outputFile } = { ...defaults, ...options }\n return {\n name: pluginName,\n apply: \"build\",\n async closeBundle() {\n try {\n await build({\n configFile: false,\n logLevel: \"warn\",\n build: {\n lib: {\n entry: path.isAbsolute(entry) ? entry : path.resolve(process.cwd(), entry),\n formats: [\"es\"],\n fileName: () => outputFile,\n },\n outDir: \"dist\",\n emptyOutDir: false,\n },\n })\n } catch (error) {\n console.error(`[${pluginName}] build failed:`, error)\n throw error\n }\n },\n }\n }\n}\n\nexport type AppBundlePluginOptions = Partial<BundlePluginOptions>\n\n/**\n * Vite plugin that produces dist/app-bundle.js after the main build for app-level integrations.\n *\n * The bundle exports `renderThumbnail(puzzleStr, inputStr?, config?)` — a pure\n * SVG-string renderer used by the Puzzmo platform for puzzle previews.\n */\nexport const appBundlePlugin = createBundlePlugin(\"app-bundle\", { entry: \"src/appBundle.js\", outputFile: \"app-bundle.js\" })\n\nexport type EditorBundlePluginOptions = Partial<BundlePluginOptions>\n\n/** Vite plugin that produces dist/editor-bundle.js after the main build for editor-level integrations. */\nexport const editorBundlePlugin = createBundlePlugin(\"editor-bundle\", { entry: \"src/editorBundle.js\", outputFile: \"editor-bundle.js\" })\n"],"mappings":";;;;;;;;;;;;;AAAA,GAAO,UAAU,EAAA;;;;;;;;;;;;;;;;;;;;SCsBL,eAAA,EAPN,IAAgB,8BAChB,IAAa,4BACb,IAAsB,OAAO;;AAGnC,SAAgB,EAAgB,IAAwC,EAAE,EAAU;CAClF,SAAS,IAAuB;EAC9B,IAAM,EAAE,oBAAA,GAAiB,IAAA,EAAW,GAAA,EAAA,EAE9B,IAAQ,CAAC,4DAA0D;AAEzE,EAAI,KACF,EAAM,KAAK,qCAAqC,KAAK,UAAU,EAAa,CAAC,oBAAoB;EAInG,IAAM,IADgB,OAAO,QAAQ,EAAO,CAAC,QAAQ,GAAG,OAAO,MAAM,KAAA,EAAU,CAC7C,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,IAAI,KAAK,UAAU,EAAE,GAAG;AAK/E,SAJI,KAAc,EAAY,KAAK,WAAW,EAE9C,EAAM,KAAK,qBAAqB,EAAY,KAAK,KAAK,CAAC,KAAK,EAErD,EAAM,KAAK,KAAK;;AAGzB,QAAO;EACL,MAAM;EACN,OAAO;EAIP,UAAU,GAAI;AACZ,OAAI,MAAO,EAAY,QAAO;;EAGhC,KAAK,GAAI;AACP,OAAI,MAAO,EAAqB,QAAO,GAAc;;EAGvD,gBAAgB,GAAQ;AAgBtB,GAfA,EAAO,YAAY,IAAI,oBAAoB,GAAM,MAAQ;AAEvD,IADA,EAAI,UAAU,gBAAgB,YAAY,EAC1C,EAAI,IAAI,uXAQS;KACjB,EAIF,EAAO,YAAY,IAAA,WAAA;yBAAW,GAAK,GAAK,GAAS;;AAC/C,WAAA,IAAI,EAAI,QAAA,OAAA,KAAA,IAAA,EAAK,MAAM,IAAI,CAAC,QAAO,EAAe,QAAO,GAAM;KAE3D,IAAM,IAAS,MAAM,EAAO,iBAAiB,EAAW;AACxD,SAAI,CAAC,EAAQ,QAAO,GAAM;AAE1B,KADA,EAAI,UAAU,gBAAgB,yBAAyB,EACvD,EAAI,IAAI,EAAO,KAAK;;oBANQ,GAAK,GAAK,GAAA;;;OAOtC;;EAMJ,qBAAqB;AACnB,UAAO,CACL;IACE,KAAK;IACL,OAAO;KAAE,MAAM;KAAU,KAAK;KAAe;IAC7C,UAAU;IACX,CACF;;EAEJ;;AAUH,SAAS,EAAmB,GAAoB,GAA+B;AAC7E,SAAQ,IAAwC,EAAE,KAAa;EAC7D,IAAM,EAAE,UAAO,kBAAA,EAAA,EAAA,EAAA,EAAoB,EAAA,EAAa,EAAS;AACzD,SAAO;GACL,MAAM;GACN,OAAO;GACP,cAAM;0BAAc;AAClB,SAAI;AACF,YAAM,EAAM;OACV,YAAY;OACZ,UAAU;OACV,OAAO;QACL,KAAK;SACH,OAAO,EAAA,QAAK,WAAW,EAAM,GAAG,IAAQ,EAAA,QAAK,QAAQ,QAAQ,KAAK,EAAE,EAAM;SAC1E,SAAS,CAAC,KAAK;SACf,gBAAgB;SACjB;QACD,QAAQ;QACR,aAAa;QACd;OACF,CAAC;cACK,GAAO;AAEd,YADA,QAAQ,MAAM,IAAI,EAAW,kBAAkB,EAAM,EAC/C;;;;GAGX;;;;;;;;;AAYL,MAAa,IAAkB,EAAmB,cAAc;CAAE,OAAO;CAAoB,YAAY;CAAiB,CAAC,EAK9G,IAAqB,EAAmB,iBAAiB;CAAE,OAAO;CAAuB,YAAY;CAAoB,CAAC"}
1
+ {"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["import type { Plugin, ResolvedConfig } from \"vite\"\nimport { build } from \"vite\"\nimport path from \"path\"\nimport fs from \"fs\"\n\nexport type PuzzmoSimulatorPluginOptions = {\n /** Whether to auto-start the game after READY (default: true) */\n autoStart?: boolean\n /** Initial collapsed state (default: true) */\n collapsed?: boolean\n /** Glob pattern for fixture files, passed to import.meta.glob which is relative to the closest puzzmo.json. Defaults to \"/fixtures/puzzles/**\\/*.json\". Pass false to disable. */\n fixturesGlob?: string | false\n}\n\nconst simulatorURL = \"/@puzzmo-simulator-init.js\"\nconst virtualID = \"virtual:puzzmo-simulator\"\n\n/** @internal */\nexport type GameInfo = {\n /** Directory containing the puzzmo.json */\n dir: string\n slug: string\n displayName: string\n /** Vite-root-relative path to app bundle entry, if it exists */\n appBundlePath: string | null\n}\n\n/**\n * Discover all games from puzzmo.json files under a root directory.\n * @internal\n */\nexport function discoverGames(viteRoot: string): Map<string, GameInfo> {\n const games = new Map<string, GameInfo>()\n const candidates = findPuzzmoJsonDirs(viteRoot, 3)\n if (fs.existsSync(path.join(viteRoot, \"puzzmo.json\"))) {\n candidates.unshift(viteRoot)\n }\n for (const dir of candidates) {\n try {\n const data = JSON.parse(fs.readFileSync(path.join(dir, \"puzzmo.json\"), \"utf-8\"))\n if (!data?.game?.slug) continue\n const bundleEntry = path.join(dir, \"src\", \"appBundle.js\")\n let appBundle: string | null = null\n if (fs.existsSync(bundleEntry)) {\n const relative = path.relative(viteRoot, bundleEntry)\n appBundle = \"/\" + relative.split(path.sep).join(\"/\")\n }\n games.set(data.game.slug, { dir, slug: data.game.slug, displayName: data.game.displayName, appBundlePath: appBundle })\n } catch {\n // skip invalid files\n }\n }\n return games\n}\n\n/**\n * Resolve which game a request belongs to using the referer URL.\n * @internal\n */\nexport function resolveGameFromReferer(referer: string | undefined, games: Map<string, GameInfo>, viteRoot: string): GameInfo | undefined {\n if (referer) {\n try {\n const refPath = new URL(referer).pathname\n for (const g of games.values()) {\n const relDir = \"/\" + path.relative(viteRoot, g.dir).split(path.sep).join(\"/\")\n if (refPath.startsWith(relDir + \"/\") || refPath === relDir) return g\n }\n } catch {\n // ignore malformed referer\n }\n }\n if (games.size === 1) return games.values().next().value\n return undefined\n}\n\n/**\n * Generate the virtual module code for the simulator.\n * @internal\n */\nexport function generateSimulatorCode(options: PuzzmoSimulatorPluginOptions, game: GameInfo | undefined): string {\n const { fixturesGlob: fixturesOpt, ...config } = options\n const fixturesGlob = fixturesOpt === false ? null : (fixturesOpt ?? \"/fixtures/puzzles/**/*.json\")\n\n const lines = [`import { createSimulator } from \"@puzzmo/sdk/simulator\"`]\n\n if (fixturesGlob) {\n lines.push(`const fixtures = import.meta.glob(${JSON.stringify(fixturesGlob)}, { eager: true })`)\n }\n\n if (game?.appBundlePath) {\n lines.push(`import(${JSON.stringify(game.appBundlePath)}).then(m => {`)\n lines.push(` if (m.renderThumbnail) globalThis.renderThumbnail = m.renderThumbnail`)\n lines.push(`}).catch(() => {})`)\n }\n\n const simConfig = { ...config, ...(game?.slug ? { slug: game.slug } : {}) }\n const configEntries = Object.entries(simConfig).filter(([, v]) => v !== undefined)\n const configParts = configEntries.map(([k, v]) => `${k}: ${JSON.stringify(v)}`)\n if (fixturesGlob) configParts.push(\"fixtures\")\n\n lines.push(`createSimulator({ ${configParts.join(\", \")} })`)\n\n return lines.join(\"\\n\")\n}\n\n/** Vite plugin that injects the Puzzmo simulator in dev mode and handles OAuth callbacks. */\nexport function puzzmoSimulator(options: PuzzmoSimulatorPluginOptions = {}): Plugin {\n let games = new Map<string, GameInfo>()\n let viteRoot: string\n\n return {\n name: \"puzzmo-simulator\",\n apply: \"serve\",\n\n configResolved(config: ResolvedConfig) {\n viteRoot = config.root\n games = discoverGames(viteRoot)\n\n if (games.size === 0) {\n config.logger.info(`\\x1b[33m\\x1b[1m PUZZMO \\x1b[22m\\x1b[39m\\x1b[2m no puzzmo.json files found\\x1b[22m`)\n } else {\n const names = [...games.values()].map((g) => `\\x1b[36m${g.slug}\\x1b[39m`)\n const label = games.size === 1 ? \"game\" : \"games\"\n config.logger.info(`\\x1b[33m\\x1b[1m PUZZMO \\x1b[22m\\x1b[39m found ${games.size} ${label}: ${names.join(\"\\x1b[2m, \\x1b[22m\")}`)\n }\n },\n\n resolveId(id) {\n if (id === virtualID || id.startsWith(virtualID + \"?\")) return \"\\0\" + id\n },\n\n load(id) {\n if (!id.startsWith(\"\\0\" + virtualID)) return\n const params = new URLSearchParams(id.split(\"?\")[1] || \"\")\n const gameSlug = params.get(\"game\")\n const game = gameSlug ? games.get(gameSlug) : games.size === 1 ? games.values().next().value : undefined\n return generateSimulatorCode(options, game)\n },\n\n configureServer(server) {\n server.middlewares.use(\"/oauth/callback\", (_req, res) => {\n res.setHeader(\"Content-Type\", \"text/html\")\n res.end(`<!DOCTYPE html>\n<html><head><title>Puzzmo OAuth</title></head>\n<body><script>\nvar params = new URLSearchParams(window.location.search);\nvar returnUrl = sessionStorage.getItem(\"oauth_return_url\") || \"/\";\nvar url = new URL(returnUrl);\nparams.forEach(function(v, k) { url.searchParams.set(k, v); });\nwindow.location.href = url.toString();\n</script></body></html>`)\n })\n\n // Serve the simulator init module, resolving the game from the referer\n server.middlewares.use(async (req, res, next) => {\n if (req.url?.split(\"?\")[0] !== simulatorURL) return next()\n\n const game = resolveGameFromReferer(req.headers.referer, games, viteRoot)\n const moduleID = virtualID + (game ? `?game=${game.slug}` : \"\")\n const result = await server.transformRequest(moduleID)\n if (!result) return next()\n res.setHeader(\"Content-Type\", \"application/javascript\")\n res.end(result.code)\n })\n },\n\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: { type: \"module\", src: simulatorURL },\n injectTo: \"head\",\n },\n ]\n },\n }\n}\n\ntype BundlePluginOptions = {\n /** Entry file for the bundle */\n entry: string\n /** Output file name */\n outputFile: string\n}\n\nfunction createBundlePlugin(pluginName: string, defaults: BundlePluginOptions) {\n return (options: Partial<BundlePluginOptions> = {}): Plugin => {\n const { entry, outputFile } = { ...defaults, ...options }\n return {\n name: pluginName,\n apply: \"build\",\n async closeBundle() {\n try {\n await build({\n configFile: false,\n logLevel: \"warn\",\n build: {\n lib: {\n entry: path.isAbsolute(entry) ? entry : path.resolve(process.cwd(), entry),\n formats: [\"es\"],\n fileName: () => outputFile,\n },\n outDir: \"dist\",\n emptyOutDir: false,\n },\n })\n } catch (error) {\n console.error(`[${pluginName}] build failed:`, error)\n throw error\n }\n },\n }\n }\n}\n\nexport type AppBundlePluginOptions = Partial<BundlePluginOptions>\n\n/**\n * Vite plugin that produces dist/app-bundle.js after the main build for app-level integrations.\n *\n * The bundle exports `renderThumbnail(puzzleStr, inputStr?, config?)` — a pure\n * SVG-string renderer used by the Puzzmo platform for puzzle previews.\n */\nexport const appBundlePlugin = createBundlePlugin(\"app-bundle\", { entry: \"src/appBundle.js\", outputFile: \"app-bundle.js\" })\n\nexport type EditorBundlePluginOptions = Partial<BundlePluginOptions>\n\n/** Vite plugin that produces dist/editor-bundle.js after the main build for editor-level integrations. */\nexport const editorBundlePlugin = createBundlePlugin(\"editor-bundle\", { entry: \"src/editorBundle.js\", outputFile: \"editor-bundle.js\" })\n\n/**\n * Recursively find directories containing puzzmo.json, up to `maxDepth` levels deep.\n * @internal\n */\nexport const findPuzzmoJsonDirs = (root: string, maxDepth: number, depth = 0): string[] => {\n if (depth >= maxDepth) return []\n const results: string[] = []\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(root, { withFileTypes: true })\n } catch {\n return results\n }\n for (const entry of entries) {\n if (!entry.isDirectory() || entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue\n const dir = path.join(root, entry.name)\n if (fs.existsSync(path.join(dir, \"puzzmo.json\"))) {\n results.push(dir)\n }\n results.push(...findPuzzmoJsonDirs(dir, maxDepth, depth + 1))\n }\n return results\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puzzmo/sdk",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "Puzzmo runtime SDK for game developers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -32,6 +32,11 @@
32
32
  "import": "./dist/fonts.js",
33
33
  "require": "./dist/fonts.cjs"
34
34
  },
35
+ "./svgJSX": {
36
+ "types": "./dist/svgJSX.d.ts",
37
+ "import": "./dist/svgJSX.js",
38
+ "require": "./dist/svgJSX.cjs"
39
+ },
35
40
  "./vite": {
36
41
  "types": "./dist/vite.d.ts",
37
42
  "import": "./dist/vite.js",
@@ -43,6 +48,7 @@
43
48
  ],
44
49
  "scripts": {
45
50
  "build": "vite build && tsgo --project tsconfig.build.json",
51
+ "test": "vitest run",
46
52
  "type-check": "tsgo --noEmit"
47
53
  },
48
54
  "keywords": [