railcode 0.1.13 → 0.1.16

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/query.js ADDED
@@ -0,0 +1,94 @@
1
+ // Pure helpers behind `railcode query list|run|create|delete`. Kept free of any
2
+ // I/O or process state so they can be unit-tested directly (see
3
+ // test/query.test.mjs); the command layer in index.ts does the HTTP and printing.
4
+ export class QueryCmdError extends Error {
5
+ }
6
+ // Matches the backend's declared param types (schemas/saved_query.py).
7
+ export const QUERY_PARAM_TYPES = ["string", "int", "float", "bool"];
8
+ // `query run --params` — the caller params as ONE JSON object. The CLI is
9
+ // mostly driven by agents, and agents speak JSON natively: no name=value
10
+ // mini-grammar, the flag takes exactly what the API takes.
11
+ export function parseRunParams(json) {
12
+ if (json === undefined)
13
+ return {};
14
+ let parsed;
15
+ try {
16
+ parsed = JSON.parse(json);
17
+ }
18
+ catch {
19
+ throw new QueryCmdError(`--params must be valid JSON. Example: --params '{"region":"emea","limit":5}'`);
20
+ }
21
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
22
+ throw new QueryCmdError(`--params must be a JSON object, e.g. '{"region":"emea"}'.`);
23
+ }
24
+ return parsed;
25
+ }
26
+ // `query create|update --params` — the declared params as a JSON array in the
27
+ // exact API shape: [{"name","type","default"?}]. A "default" makes the param
28
+ // optional at invoke time (the server binds it when a caller omits the param);
29
+ // the backend re-validates it against the declared type at save.
30
+ export function parseParamDecls(json) {
31
+ if (json === undefined)
32
+ return [];
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(json);
36
+ }
37
+ catch {
38
+ throw new QueryCmdError(`--params must be valid JSON. Example: --params '[{"name":"region","type":"string","default":"emea"}]'`);
39
+ }
40
+ if (!Array.isArray(parsed)) {
41
+ throw new QueryCmdError(`--params must be a JSON array of {"name","type","default"?} objects.`);
42
+ }
43
+ return parsed.map((item, i) => {
44
+ if (item === null || typeof item !== "object" || Array.isArray(item)) {
45
+ throw new QueryCmdError(`--params[${i}] must be an object like {"name":"region","type":"string"}.`);
46
+ }
47
+ const { name, type, default: def, ...extra } = item;
48
+ if (typeof name !== "string" || name === "") {
49
+ throw new QueryCmdError(`--params[${i}] needs a non-empty string "name".`);
50
+ }
51
+ if (typeof type !== "string" || !QUERY_PARAM_TYPES.includes(type)) {
52
+ throw new QueryCmdError(`--params[${i}] ("${name}") needs "type": one of ${QUERY_PARAM_TYPES.join(", ")}.`);
53
+ }
54
+ const extraKeys = Object.keys(extra);
55
+ if (extraKeys.length > 0) {
56
+ throw new QueryCmdError(`--params[${i}] ("${name}") has unknown key(s): ${extraKeys.join(", ")}.`);
57
+ }
58
+ const decl = { name, type: type };
59
+ if (def !== undefined) {
60
+ if (def === null || !["string", "number", "boolean"].includes(typeof def)) {
61
+ throw new QueryCmdError(`--params[${i}] ("${name}") "default" must be a string, number or boolean — omit it to make the param required.`);
62
+ }
63
+ decl.default = def;
64
+ }
65
+ return decl;
66
+ });
67
+ }
68
+ export function pickAuthoringSql(sql, sqlFile, required) {
69
+ if (sql !== undefined && sqlFile !== undefined) {
70
+ throw new QueryCmdError("Pass the SQL template with --sql or --sql-file, not both.");
71
+ }
72
+ if (sql !== undefined) {
73
+ const trimmed = sql.trim();
74
+ if (!trimmed)
75
+ throw new QueryCmdError("--sql is empty.");
76
+ return { kind: "inline", sql: trimmed };
77
+ }
78
+ if (sqlFile !== undefined) {
79
+ return { kind: "file", path: sqlFile };
80
+ }
81
+ if (required) {
82
+ throw new QueryCmdError('Provide the SQL template: --sql "select ..." or --sql-file query.sql.');
83
+ }
84
+ return null;
85
+ }
86
+ // Render one saved query's signature for the list table, e.g.
87
+ // "region?: string = "emea", limit: int" — `?` marks optional (defaulted) params.
88
+ export function paramSignature(params) {
89
+ return (params
90
+ .map((p) => p.default === undefined
91
+ ? `${p.name}: ${p.type}`
92
+ : `${p.name}?: ${p.type} = ${JSON.stringify(p.default)}`)
93
+ .join(", ") || "-");
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "railcode",
3
- "version": "0.1.13",
3
+ "version": "0.1.16",
4
4
  "description": "Developer CLI for the multi-tenant Railcode platform: log in, scaffold, and deploy static apps.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,7 +13,7 @@
13
13
  "README.md"
14
14
  ],
15
15
  "scripts": {
16
- "build:sdk": "node ../sdk/build.mjs && mkdir -p static && cp ../sdk/dist/sdk.js static/sdk.js",
16
+ "build:sdk": "node ../sdk/build.mjs && mkdir -p static && cp ../sdk/dist/sdk.js static/sdk.js && rm -rf static/react-template-assets && cp -R assets/react-template static/react-template-assets",
17
17
  "prebuild": "pnpm run build:sdk",
18
18
  "build": "tsc -p tsconfig.json",
19
19
  "postbuild": "chmod +x dist/index.js",
@@ -0,0 +1 @@
1
+ <svg width="50" height="30" viewBox="0 0 50 30" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.8914 17.2057c-.3685.7371-1.42031.7371-1.78884 0L8.2212 15.443c-.14077-.2815-.14077-.6129 0-.8944l.88136-1.7627c.36853-.7371 1.42034-.7371 1.78884 0l.8814 1.7627c.1407.2815.1407.6129 0 .8944l-.8814 1.7627zM10.8914 27.2028c-.3685.737-1.42031.737-1.78884 0L8.2212 25.44c-.14077-.2815-.14077-.6129 0-.8944l.88136-1.7627c.36853-.7371 1.42034-.7371 1.78884 0l.8814 1.7627c.1407.2815.1407.6129 0 .8944l-.8814 1.7628z" fill="#1D4AFF"/><path d="M0 23.4082c0-.8909 1.07714-1.3371 1.70711-.7071l4.58338 4.5834c.62997.63.1838 1.7071-.7071 1.7071H.999999c-.552284 0-.999999-.4477-.999999-1v-4.5834zm0-4.8278c0 .2652.105357.5196.292893.7071l9.411217 9.4112c.18753.1875.44189.2929.70709.2929h5.1692c.8909 0 1.3371-1.0771.7071-1.7071L1.70711 12.7041C1.07714 12.0741 0 12.5203 0 13.4112v5.1692zm0-9.99701c0 .26521.105357.51957.292893.7071L19.7011 28.6987c.1875.1875.4419.2929.7071.2929h5.1692c.8909 0 1.3371-1.0771.7071-1.7071L1.70711 2.70711C1.07715 2.07715 0 2.52331 0 3.41421v5.16918zm9.997 0c0 .26521.1054.51957.2929.7071l17.994 17.99401c.63.63 1.7071.1838 1.7071-.7071v-5.1692c0-.2652-.1054-.5196-.2929-.7071l-17.994-17.994c-.63-.62996-1.7071-.18379-1.7071.70711v5.16918zm11.7041-5.87628c-.63-.62997-1.7071-.1838-1.7071.7071v5.16918c0 .26521.1054.51957.2929.7071l7.997 7.99701c.63.63 1.7071.1838 1.7071-.7071v-5.1692c0-.2652-.1054-.5196-.2929-.7071l-7.997-7.99699z" fill="#F9BD2B"/><path d="M42.5248 23.5308l-9.4127-9.4127c-.63-.63-1.7071-.1838-1.7071.7071v13.1664c0 .5523.4477 1 1 1h14.5806c.5523 0 1-.4477 1-1v-1.199c0-.5523-.4496-.9934-.9973-1.0647-1.6807-.2188-3.2528-.9864-4.4635-2.1971zm-6.3213 2.2618c-.8829 0-1.5995-.7166-1.5995-1.5996 0-.8829.7166-1.5995 1.5995-1.5995.883 0 1.5996.7166 1.5996 1.5995 0 .883-.7166 1.5996-1.5996 1.5996z" fill="#000"/><path d="M0 27.9916c0 .5523.447715 1 1 1h4.58339c.8909 0 1.33707-1.0771.70711-1.7071l-4.58339-4.5834C1.07714 22.0711 0 22.5173 0 23.4082v4.5834zM9.997 10.997L1.70711 2.70711C1.07714 2.07714 0 2.52331 0 3.41421v5.16918c0 .26521.105357.51957.292893.7071L9.997 18.9946V10.997zM1.70711 12.7041C1.07714 12.0741 0 12.5203 0 13.4112v5.1692c0 .2652.105357.5196.292893.7071L9.997 28.9916V20.994l-8.28989-8.2899z" fill="#1D4AFF"/><path d="M19.994 11.4112c0-.2652-.1053-.5196-.2929-.7071l-7.997-7.99699c-.6299-.62997-1.70709-.1838-1.70709.7071v5.16918c0 .26521.10539.51957.29289.7071l9.7041 9.70411v-7.5834zM9.99701 28.9916h5.58339c.8909 0 1.3371-1.0771.7071-1.7071L9.99701 20.994v7.9976zM9.99701 10.997v7.5834c0 .2652.10539.5196.29289.7071l9.7041 9.7041v-7.5834c0-.2652-.1053-.5196-.2929-.7071L9.99701 10.997z" fill="#F54E00"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1800" height="1800" fill="none" viewBox="0 0 1800 1800"><path fill="#000" d="M1000.46 450C1174.77 450 1278.43 553.669 1278.43 691.282C1278.43 828.896 1174.77 932.563 1000.46 932.563H912.382L1350 1350H1040.82L707.794 1033.48C683.944 1011.47 672.936 985.781 672.935 963.765C672.935 932.572 694.959 905.049 737.161 893.122L908.712 847.244C973.85 829.812 1018.81 779.353 1018.81 713.298C1018.8 632.567 952.745 585.78 871.095 585.78H450V450H1000.46Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" fill="#000" role="img" viewBox="0 0 24 24"><title>OpenAI icon</title><path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="576.095" height="593.844" viewBox="0 0 432.071 445.383" xml:space="preserve"><g id="Layer_x0020_3" style="fill-rule:nonzero;clip-rule:nonzero;fill:none;stroke:#fff;stroke-width:12.4651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4"><path style="fill:#000;stroke:#000;stroke-width:37.3953;stroke-linecap:butt;stroke-linejoin:miter" d="M323.205,324.227c2.833-23.601,1.984-27.062,19.563-23.239l4.463,0.392c13.517,0.615,31.199-2.174,41.587-7c22.362-10.376,35.622-27.7,13.572-23.148c-50.297,10.376-53.755-6.655-53.755-6.655c53.111-78.803,75.313-178.836,56.149-203.322 C352.514-5.534,262.036,26.049,260.522,26.869l-0.482,0.089c-9.938-2.062-21.06-3.294-33.554-3.496c-22.761-0.374-40.032,5.967-53.133,15.904c0,0-161.408-66.498-153.899,83.628c1.597,31.936,45.777,241.655,98.47,178.31 c19.259-23.163,37.871-42.748,37.871-42.748c9.242,6.14,20.307,9.272,31.912,8.147l0.897-0.765c-0.281,2.876-0.157,5.689,0.359,9.019c-13.572,15.167-9.584,17.83-36.723,23.416c-27.457,5.659-11.326,15.734-0.797,18.367c12.768,3.193,42.305,7.716,62.268-20.224 l-0.795,3.188c5.325,4.26,4.965,30.619,5.72,49.452c0.756,18.834,2.017,36.409,5.856,46.771c3.839,10.36,8.369,37.05,44.036,29.406c29.809-6.388,52.6-15.582,54.677-101.107"/><path style="fill:#336791;stroke:none" d="M402.395,271.23c-50.302,10.376-53.76-6.655-53.76-6.655c53.111-78.808,75.313-178.843,56.153-203.326c-52.27-66.785-142.752-35.2-144.262-34.38l-0.486,0.087c-9.938-2.063-21.06-3.292-33.56-3.496c-22.761-0.373-40.026,5.967-53.127,15.902 c0,0-161.411-66.495-153.904,83.63c1.597,31.938,45.776,241.657,98.471,178.312c19.26-23.163,37.869-42.748,37.869-42.748c9.243,6.14,20.308,9.272,31.908,8.147l0.901-0.765c-0.28,2.876-0.152,5.689,0.361,9.019c-13.575,15.167-9.586,17.83-36.723,23.416 c-27.459,5.659-11.328,15.734-0.796,18.367c12.768,3.193,42.307,7.716,62.266-20.224l-0.796,3.188c5.319,4.26,9.054,27.711,8.428,48.969c-0.626,21.259-1.044,35.854,3.147,47.254c4.191,11.4,8.368,37.05,44.042,29.406c29.809-6.388,45.256-22.942,47.405-50.555 c1.525-19.631,4.976-16.729,5.194-34.28l2.768-8.309c3.192-26.611,0.507-35.196,18.872-31.203l4.463,0.392c13.517,0.615,31.208-2.174,41.591-7c22.358-10.376,35.618-27.7,13.573-23.148z"/><path d="M215.866,286.484c-1.385,49.516,0.348,99.377,5.193,111.495c4.848,12.118,15.223,35.688,50.9,28.045c29.806-6.39,40.651-18.756,45.357-46.051c3.466-20.082,10.148-75.854,11.005-87.281"/><path d="M173.104,38.256c0,0-161.521-66.016-154.012,84.109c1.597,31.938,45.779,241.664,98.473,178.316c19.256-23.166,36.671-41.335,36.671-41.335"/><path d="M260.349,26.207c-5.591,1.753,89.848-34.889,144.087,34.417c19.159,24.484-3.043,124.519-56.153,203.329"/><path style="stroke-linejoin:bevel" d="M348.282,263.953c0,0,3.461,17.036,53.764,6.653c22.04-4.552,8.776,12.774-13.577,23.155c-18.345,8.514-59.474,10.696-60.146-1.069c-1.729-30.355,21.647-21.133,19.96-28.739c-1.525-6.85-11.979-13.573-18.894-30.338 c-6.037-14.633-82.796-126.849,21.287-110.183c3.813-0.789-27.146-99.002-124.553-100.599c-97.385-1.597-94.19,119.762-94.19,119.762"/><path d="M188.604,274.334c-13.577,15.166-9.584,17.829-36.723,23.417c-27.459,5.66-11.326,15.733-0.797,18.365c12.768,3.195,42.307,7.718,62.266-20.229c6.078-8.509-0.036-22.086-8.385-25.547c-4.034-1.671-9.428-3.765-16.361,3.994z"/><path d="M187.715,274.069c-1.368-8.917,2.93-19.528,7.536-31.942c6.922-18.626,22.893-37.255,10.117-96.339c-9.523-44.029-73.396-9.163-73.436-3.193c-0.039,5.968,2.889,30.26-1.067,58.548c-5.162,36.913,23.488,68.132,56.479,64.938"/><path style="fill:#fff;stroke-width:4.155;stroke-linecap:butt;stroke-linejoin:miter" d="M172.517,141.7c-0.288,2.039,3.733,7.48,8.976,8.207c5.234,0.73,9.714-3.522,9.998-5.559c0.284-2.039-3.732-4.285-8.977-5.015c-5.237-0.731-9.719,0.333-9.996,2.367z"/><path style="fill:#fff;stroke-width:2.0775;stroke-linecap:butt;stroke-linejoin:miter" d="M331.941,137.543c0.284,2.039-3.732,7.48-8.976,8.207c-5.238,0.73-9.718-3.522-10.005-5.559c-0.277-2.039,3.74-4.285,8.979-5.015c5.239-0.73,9.718,0.333,10.002,2.368z"/><path d="M350.676,123.432c0.863,15.994-3.445,26.888-3.988,43.914c-0.804,24.748,11.799,53.074-7.191,81.435"/><path style="stroke-width:3" d="M0,60.232"/></g></svg>
package/static/sdk.js CHANGED
@@ -120,10 +120,10 @@
120
120
  this.name = "ApiError";
121
121
  }
122
122
  };
123
- function buildUrl(path, query) {
123
+ function buildUrl(path, query2) {
124
124
  const url2 = new URL(BASE + path, location.origin);
125
- if (query) {
126
- for (const [key, value] of Object.entries(query)) {
125
+ if (query2) {
126
+ for (const [key, value] of Object.entries(query2)) {
127
127
  if (value !== void 0) url2.searchParams.set(key, String(value));
128
128
  }
129
129
  }
@@ -148,8 +148,8 @@
148
148
  if (resp.status === 204) return void 0;
149
149
  return await resp.json();
150
150
  }
151
- async function getOrNull(path, query) {
152
- const resp = await send(buildUrl(path, query), { credentials: "include" });
151
+ async function getOrNull(path, query2) {
152
+ const resp = await send(buildUrl(path, query2), { credentials: "include" });
153
153
  if (resp.status === 404) return null;
154
154
  if (!resp.ok) throw new ApiError(resp.status, await resp.text());
155
155
  return await resp.json();
@@ -232,8 +232,8 @@
232
232
  rows.truncated = r.truncated;
233
233
  return rows;
234
234
  };
235
- var runSQL = (engine, connection, query, params) => {
236
- const trimmed = shorten(query.replace(/\s+/g, " ").trim(), QUERY_LABEL_MAX);
235
+ var runSQL = (engine, connection, query2, params) => {
236
+ const trimmed = shorten(query2.replace(/\s+/g, " ").trim(), QUERY_LABEL_MAX);
237
237
  const label2 = `${engine ?? "data"}(${q(connection)}).runSQL(${q(trimmed)}${params && params.length ? `, ${shorten(JSON.stringify(params), PARAMS_LABEL_MAX)}` : ""})`;
238
238
  return track(
239
239
  "sql",
@@ -241,16 +241,16 @@
241
241
  () => call("POST", "/sql", {
242
242
  // engine is advisory; omit it for the generic data() namespace so the backend
243
243
  // dispatches on the connection's stored kind. postgres() pins engine="postgres".
244
- body: { ...engine ? { engine } : {}, connection, query, params: params || [] }
244
+ body: { ...engine ? { engine } : {}, connection, query: query2, params: params || [] }
245
245
  }).then(toSqlRows)
246
246
  );
247
247
  };
248
248
  var namespace = (engine) => {
249
249
  const handle = (connection) => ({
250
- runSQL: (query, params) => runSQL(engine, connection, query, params)
250
+ runSQL: (query2, params) => runSQL(engine, connection, query2, params)
251
251
  });
252
252
  const ns = handle;
253
- ns.runSQL = (query, params) => runSQL(engine, "default", query, params);
253
+ ns.runSQL = (query2, params) => runSQL(engine, "default", query2, params);
254
254
  return ns;
255
255
  };
256
256
  var data = namespace();
@@ -439,6 +439,7 @@
439
439
  function body(input, opts = {}) {
440
440
  const out = Array.isArray(input) ? { messages: input } : { input };
441
441
  if (opts.model !== void 0) out.model = opts.model;
442
+ if (opts.provider !== void 0) out.provider = opts.provider;
442
443
  if (opts.system !== void 0) out.system = opts.system;
443
444
  if (opts.output !== void 0) out.output = opts.output;
444
445
  if (opts.temperature !== void 0) out.temperature = opts.temperature;
@@ -498,6 +499,21 @@
498
499
  return trackStream("llm", label("stream", input, opts), () => doStream(input, opts));
499
500
  }
500
501
  var llm = { generate, stream };
502
+ var llmProviders = () => track("llm", "llmProviders()", () => call("GET", "/llm/providers"));
503
+
504
+ // ../sdk/src/queries.ts
505
+ var PARAMS_LABEL_MAX2 = 40;
506
+ var query = (name, params) => {
507
+ const label2 = `query(${q(name)}${params && Object.keys(params).length ? `, ${shorten(JSON.stringify(params), PARAMS_LABEL_MAX2)}` : ""})`;
508
+ return track(
509
+ "sql",
510
+ label2,
511
+ () => call("POST", `/queries/${encPath(name)}`, {
512
+ body: { params: params || {} }
513
+ }).then(toSqlRows)
514
+ );
515
+ };
516
+ var savedQueries = () => track("queries", "savedQueries()", () => call("GET", "/queries"));
501
517
 
502
518
  // ../sdk/src/service-connectors.ts
503
519
  var PATH_LABEL_MAX = 60;
@@ -540,6 +556,7 @@
540
556
  target.appUsers = appUsers;
541
557
  target.designSystem = designSystem;
542
558
  target.llm = llm;
559
+ target.llmProviders = llmProviders;
543
560
  target.data = data;
544
561
  target.postgres = postgres;
545
562
  target.bigquery = bigquery;
@@ -547,4 +564,6 @@
547
564
  target.dataConnectors = dataConnectors;
548
565
  target.connector = connector;
549
566
  target.serviceConnectors = serviceConnectors;
567
+ target.query = query;
568
+ target.savedQueries = savedQueries;
550
569
  })();