@lotics/cli 0.44.2 → 0.45.0
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/client.d.ts +11 -0
- package/dist/client.js +18 -0
- package/dist/dev/rpc_handler.d.ts +1 -1
- package/dist/dev/rpc_handler.js +56 -0
- package/dist/dev/rpc_handler.test.js +30 -0
- package/dist/src/cli.js +93 -5
- package/dist/starter_template.d.ts +1 -1
- package/dist/starter_template.js +1 -1
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -218,6 +218,17 @@ export declare class LoticsClient {
|
|
|
218
218
|
thumbnail_url?: string;
|
|
219
219
|
};
|
|
220
220
|
}>;
|
|
221
|
+
appGetRecordComments(app_id: string, record_id: string): Promise<unknown[]>;
|
|
222
|
+
appCreateRecordComment(app_id: string, record_id: string, body: {
|
|
223
|
+
content: string;
|
|
224
|
+
file_ids?: string[];
|
|
225
|
+
}): Promise<unknown>;
|
|
226
|
+
appUpdateRecordComment(app_id: string, record_id: string, comment_id: string, body: {
|
|
227
|
+
content: string;
|
|
228
|
+
files?: unknown[];
|
|
229
|
+
}): Promise<unknown>;
|
|
230
|
+
appDeleteRecordComment(app_id: string, record_id: string, comment_id: string): Promise<void>;
|
|
231
|
+
appGetTableCommentCounts(app_id: string, table_id: string): Promise<Record<string, number>>;
|
|
221
232
|
deployAppVersion(args: {
|
|
222
233
|
app_id: string;
|
|
223
234
|
source_archive: Buffer;
|
package/dist/client.js
CHANGED
|
@@ -208,6 +208,24 @@ export class LoticsClient {
|
|
|
208
208
|
async appCompleteFileUpload(app_id, body) {
|
|
209
209
|
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/files/complete`, body);
|
|
210
210
|
}
|
|
211
|
+
// App-scoped record comments — the `lotics app dev` loop forwards the iframe's
|
|
212
|
+
// `comments.*` ops to these (production routes them through the iframe host).
|
|
213
|
+
// App authority + tenant floor are enforced server-side; these are thin.
|
|
214
|
+
async appGetRecordComments(app_id, record_id) {
|
|
215
|
+
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/records/${encodeURIComponent(record_id)}/comments`);
|
|
216
|
+
}
|
|
217
|
+
async appCreateRecordComment(app_id, record_id, body) {
|
|
218
|
+
return this.request("POST", `/v1/apps/${encodeURIComponent(app_id)}/records/${encodeURIComponent(record_id)}/comments`, body);
|
|
219
|
+
}
|
|
220
|
+
async appUpdateRecordComment(app_id, record_id, comment_id, body) {
|
|
221
|
+
return this.request("PATCH", `/v1/apps/${encodeURIComponent(app_id)}/records/${encodeURIComponent(record_id)}/comments/${encodeURIComponent(comment_id)}`, body);
|
|
222
|
+
}
|
|
223
|
+
async appDeleteRecordComment(app_id, record_id, comment_id) {
|
|
224
|
+
await this.request("DELETE", `/v1/apps/${encodeURIComponent(app_id)}/records/${encodeURIComponent(record_id)}/comments/${encodeURIComponent(comment_id)}`);
|
|
225
|
+
}
|
|
226
|
+
async appGetTableCommentCounts(app_id, table_id) {
|
|
227
|
+
return this.request("GET", `/v1/apps/${encodeURIComponent(app_id)}/tables/${encodeURIComponent(table_id)}/comment-counts`);
|
|
228
|
+
}
|
|
211
229
|
async deployAppVersion(args) {
|
|
212
230
|
const formData = new FormData();
|
|
213
231
|
// Wrap Buffers as Uint8Array views so the Blob constructor accepts them
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* { message }. Same shape as the production iframe-host's error path.
|
|
7
7
|
*/
|
|
8
8
|
import { LoticsClient } from "../client.js";
|
|
9
|
-
export type RpcOp = "query" | "workflow" | "members" | "context" | "upload_url" | "upload_complete";
|
|
9
|
+
export type RpcOp = "query" | "workflow" | "members" | "context" | "upload_url" | "upload_complete" | "comments.list" | "comments.create" | "comments.update" | "comments.delete" | "comments.counts";
|
|
10
10
|
export interface RpcRequest {
|
|
11
11
|
app_id: string;
|
|
12
12
|
op: RpcOp;
|
package/dist/dev/rpc_handler.js
CHANGED
|
@@ -12,6 +12,11 @@ const SUPPORTED_OPS = new Set([
|
|
|
12
12
|
"context",
|
|
13
13
|
"upload_url",
|
|
14
14
|
"upload_complete",
|
|
15
|
+
"comments.list",
|
|
16
|
+
"comments.create",
|
|
17
|
+
"comments.update",
|
|
18
|
+
"comments.delete",
|
|
19
|
+
"comments.counts",
|
|
15
20
|
]);
|
|
16
21
|
export async function dispatchRpc(client, body, opts) {
|
|
17
22
|
if (!body || typeof body.app_id !== "string" || typeof body.op !== "string") {
|
|
@@ -86,6 +91,57 @@ export async function dispatchRpc(client, body, opts) {
|
|
|
86
91
|
filename: p.filename,
|
|
87
92
|
});
|
|
88
93
|
}
|
|
94
|
+
// Comment ops mirror the production iframe-host's `handleCommentRpc` return
|
|
95
|
+
// shapes so the SDK hooks behave identically in dev. App authority + the
|
|
96
|
+
// `comments` capability + tenant floor are enforced server-side.
|
|
97
|
+
case "comments.list": {
|
|
98
|
+
const p = body.payload;
|
|
99
|
+
if (!p || typeof p.record_id !== "string") {
|
|
100
|
+
throw new Error("comments.list payload must include record_id");
|
|
101
|
+
}
|
|
102
|
+
const comments = await client.appGetRecordComments(body.app_id, p.record_id);
|
|
103
|
+
return { comments };
|
|
104
|
+
}
|
|
105
|
+
case "comments.create": {
|
|
106
|
+
const p = body.payload;
|
|
107
|
+
if (!p || typeof p.record_id !== "string" || typeof p.content !== "string") {
|
|
108
|
+
throw new Error("comments.create payload must include record_id and content");
|
|
109
|
+
}
|
|
110
|
+
const created = await client.appCreateRecordComment(body.app_id, p.record_id, {
|
|
111
|
+
content: p.content,
|
|
112
|
+
file_ids: p.file_ids,
|
|
113
|
+
});
|
|
114
|
+
return { comments: created ? [created] : [] };
|
|
115
|
+
}
|
|
116
|
+
case "comments.update": {
|
|
117
|
+
const p = body.payload;
|
|
118
|
+
if (!p ||
|
|
119
|
+
typeof p.record_id !== "string" ||
|
|
120
|
+
typeof p.comment_id !== "string" ||
|
|
121
|
+
typeof p.content !== "string") {
|
|
122
|
+
throw new Error("comments.update payload must include record_id, comment_id, content");
|
|
123
|
+
}
|
|
124
|
+
const comment = await client.appUpdateRecordComment(body.app_id, p.record_id, p.comment_id, {
|
|
125
|
+
content: p.content,
|
|
126
|
+
files: p.files,
|
|
127
|
+
});
|
|
128
|
+
return { comment };
|
|
129
|
+
}
|
|
130
|
+
case "comments.delete": {
|
|
131
|
+
const p = body.payload;
|
|
132
|
+
if (!p || typeof p.record_id !== "string" || typeof p.comment_id !== "string") {
|
|
133
|
+
throw new Error("comments.delete payload must include record_id and comment_id");
|
|
134
|
+
}
|
|
135
|
+
await client.appDeleteRecordComment(body.app_id, p.record_id, p.comment_id);
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
case "comments.counts": {
|
|
139
|
+
const p = body.payload;
|
|
140
|
+
if (!p || typeof p.table_id !== "string") {
|
|
141
|
+
throw new Error("comments.counts payload must include table_id");
|
|
142
|
+
}
|
|
143
|
+
return client.appGetTableCommentCounts(body.app_id, p.table_id);
|
|
144
|
+
}
|
|
89
145
|
default: {
|
|
90
146
|
// Unreachable — SUPPORTED_OPS gates above.
|
|
91
147
|
throw new Error(`Unhandled RPC op: ${body.op}`);
|
|
@@ -26,3 +26,33 @@ describe("dispatchRpc — context op", () => {
|
|
|
26
26
|
await expect(dispatchRpc(client, { app_id: "app_x", op: "bogus", payload: {} })).rejects.toThrow(/Unknown RPC op/);
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
|
+
describe("dispatchRpc — comment ops", () => {
|
|
30
|
+
it("comments.list forwards to appGetRecordComments, wrapped as { comments }", async () => {
|
|
31
|
+
const comments = [{ id: "cmt_1", content: "hi" }];
|
|
32
|
+
const client = mockClient({ appGetRecordComments: async () => comments });
|
|
33
|
+
const result = await dispatchRpc(client, {
|
|
34
|
+
app_id: "app_x",
|
|
35
|
+
op: "comments.list",
|
|
36
|
+
payload: { record_id: "rec_1" },
|
|
37
|
+
});
|
|
38
|
+
expect(result).toEqual({ comments });
|
|
39
|
+
});
|
|
40
|
+
it("comments.counts forwards to appGetTableCommentCounts (counts, no content)", async () => {
|
|
41
|
+
const counts = { rec_1: 2, rec_2: 1 };
|
|
42
|
+
const client = mockClient({ appGetTableCommentCounts: async () => counts });
|
|
43
|
+
const result = await dispatchRpc(client, {
|
|
44
|
+
app_id: "app_x",
|
|
45
|
+
op: "comments.counts",
|
|
46
|
+
payload: { table_id: "tbl_1" },
|
|
47
|
+
});
|
|
48
|
+
expect(result).toEqual(counts);
|
|
49
|
+
});
|
|
50
|
+
it("requires the right payload per comment op", async () => {
|
|
51
|
+
const client = mockClient({
|
|
52
|
+
appGetRecordComments: async () => [],
|
|
53
|
+
appGetTableCommentCounts: async () => ({}),
|
|
54
|
+
});
|
|
55
|
+
await expect(dispatchRpc(client, { app_id: "app_x", op: "comments.list", payload: {} })).rejects.toThrow(/record_id/);
|
|
56
|
+
await expect(dispatchRpc(client, { app_id: "app_x", op: "comments.counts", payload: {} })).rejects.toThrow(/table_id/);
|
|
57
|
+
});
|
|
58
|
+
});
|
package/dist/src/cli.js
CHANGED
|
@@ -29801,6 +29801,41 @@ var LoticsClient = class {
|
|
|
29801
29801
|
body
|
|
29802
29802
|
);
|
|
29803
29803
|
}
|
|
29804
|
+
// App-scoped record comments — the `lotics app dev` loop forwards the iframe's
|
|
29805
|
+
// `comments.*` ops to these (production routes them through the iframe host).
|
|
29806
|
+
// App authority + tenant floor are enforced server-side; these are thin.
|
|
29807
|
+
async appGetRecordComments(app_id, record_id) {
|
|
29808
|
+
return this.request(
|
|
29809
|
+
"GET",
|
|
29810
|
+
`/v1/apps/${encodeURIComponent(app_id)}/records/${encodeURIComponent(record_id)}/comments`
|
|
29811
|
+
);
|
|
29812
|
+
}
|
|
29813
|
+
async appCreateRecordComment(app_id, record_id, body) {
|
|
29814
|
+
return this.request(
|
|
29815
|
+
"POST",
|
|
29816
|
+
`/v1/apps/${encodeURIComponent(app_id)}/records/${encodeURIComponent(record_id)}/comments`,
|
|
29817
|
+
body
|
|
29818
|
+
);
|
|
29819
|
+
}
|
|
29820
|
+
async appUpdateRecordComment(app_id, record_id, comment_id, body) {
|
|
29821
|
+
return this.request(
|
|
29822
|
+
"PATCH",
|
|
29823
|
+
`/v1/apps/${encodeURIComponent(app_id)}/records/${encodeURIComponent(record_id)}/comments/${encodeURIComponent(comment_id)}`,
|
|
29824
|
+
body
|
|
29825
|
+
);
|
|
29826
|
+
}
|
|
29827
|
+
async appDeleteRecordComment(app_id, record_id, comment_id) {
|
|
29828
|
+
await this.request(
|
|
29829
|
+
"DELETE",
|
|
29830
|
+
`/v1/apps/${encodeURIComponent(app_id)}/records/${encodeURIComponent(record_id)}/comments/${encodeURIComponent(comment_id)}`
|
|
29831
|
+
);
|
|
29832
|
+
}
|
|
29833
|
+
async appGetTableCommentCounts(app_id, table_id) {
|
|
29834
|
+
return this.request(
|
|
29835
|
+
"GET",
|
|
29836
|
+
`/v1/apps/${encodeURIComponent(app_id)}/tables/${encodeURIComponent(table_id)}/comment-counts`
|
|
29837
|
+
);
|
|
29838
|
+
}
|
|
29804
29839
|
async deployAppVersion(args) {
|
|
29805
29840
|
const formData = new FormData();
|
|
29806
29841
|
formData.append(
|
|
@@ -30179,7 +30214,7 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
30179
30214
|
import { tmpdir } from "node:os";
|
|
30180
30215
|
|
|
30181
30216
|
// src/starter_template.ts
|
|
30182
|
-
var STARTER_FALLBACK_UI_VERSION = "
|
|
30217
|
+
var STARTER_FALLBACK_UI_VERSION = "2.3.0";
|
|
30183
30218
|
var STARTER_FALLBACK_SDK_VERSION = "0.11.0";
|
|
30184
30219
|
var STARTER_REACT_NATIVE_VERSION = "0.85.3";
|
|
30185
30220
|
function buildStarterTemplate(args) {
|
|
@@ -30691,7 +30726,12 @@ var SUPPORTED_OPS = /* @__PURE__ */ new Set([
|
|
|
30691
30726
|
"members",
|
|
30692
30727
|
"context",
|
|
30693
30728
|
"upload_url",
|
|
30694
|
-
"upload_complete"
|
|
30729
|
+
"upload_complete",
|
|
30730
|
+
"comments.list",
|
|
30731
|
+
"comments.create",
|
|
30732
|
+
"comments.update",
|
|
30733
|
+
"comments.delete",
|
|
30734
|
+
"comments.counts"
|
|
30695
30735
|
]);
|
|
30696
30736
|
async function dispatchRpc(client, body, opts) {
|
|
30697
30737
|
if (!body || typeof body.app_id !== "string" || typeof body.op !== "string") {
|
|
@@ -30758,6 +30798,54 @@ async function dispatchRpc(client, body, opts) {
|
|
|
30758
30798
|
filename: p.filename
|
|
30759
30799
|
});
|
|
30760
30800
|
}
|
|
30801
|
+
// Comment ops mirror the production iframe-host's `handleCommentRpc` return
|
|
30802
|
+
// shapes so the SDK hooks behave identically in dev. App authority + the
|
|
30803
|
+
// `comments` capability + tenant floor are enforced server-side.
|
|
30804
|
+
case "comments.list": {
|
|
30805
|
+
const p = body.payload;
|
|
30806
|
+
if (!p || typeof p.record_id !== "string") {
|
|
30807
|
+
throw new Error("comments.list payload must include record_id");
|
|
30808
|
+
}
|
|
30809
|
+
const comments = await client.appGetRecordComments(body.app_id, p.record_id);
|
|
30810
|
+
return { comments };
|
|
30811
|
+
}
|
|
30812
|
+
case "comments.create": {
|
|
30813
|
+
const p = body.payload;
|
|
30814
|
+
if (!p || typeof p.record_id !== "string" || typeof p.content !== "string") {
|
|
30815
|
+
throw new Error("comments.create payload must include record_id and content");
|
|
30816
|
+
}
|
|
30817
|
+
const created = await client.appCreateRecordComment(body.app_id, p.record_id, {
|
|
30818
|
+
content: p.content,
|
|
30819
|
+
file_ids: p.file_ids
|
|
30820
|
+
});
|
|
30821
|
+
return { comments: created ? [created] : [] };
|
|
30822
|
+
}
|
|
30823
|
+
case "comments.update": {
|
|
30824
|
+
const p = body.payload;
|
|
30825
|
+
if (!p || typeof p.record_id !== "string" || typeof p.comment_id !== "string" || typeof p.content !== "string") {
|
|
30826
|
+
throw new Error("comments.update payload must include record_id, comment_id, content");
|
|
30827
|
+
}
|
|
30828
|
+
const comment = await client.appUpdateRecordComment(body.app_id, p.record_id, p.comment_id, {
|
|
30829
|
+
content: p.content,
|
|
30830
|
+
files: p.files
|
|
30831
|
+
});
|
|
30832
|
+
return { comment };
|
|
30833
|
+
}
|
|
30834
|
+
case "comments.delete": {
|
|
30835
|
+
const p = body.payload;
|
|
30836
|
+
if (!p || typeof p.record_id !== "string" || typeof p.comment_id !== "string") {
|
|
30837
|
+
throw new Error("comments.delete payload must include record_id and comment_id");
|
|
30838
|
+
}
|
|
30839
|
+
await client.appDeleteRecordComment(body.app_id, p.record_id, p.comment_id);
|
|
30840
|
+
return void 0;
|
|
30841
|
+
}
|
|
30842
|
+
case "comments.counts": {
|
|
30843
|
+
const p = body.payload;
|
|
30844
|
+
if (!p || typeof p.table_id !== "string") {
|
|
30845
|
+
throw new Error("comments.counts payload must include table_id");
|
|
30846
|
+
}
|
|
30847
|
+
return client.appGetTableCommentCounts(body.app_id, p.table_id);
|
|
30848
|
+
}
|
|
30761
30849
|
default: {
|
|
30762
30850
|
throw new Error(`Unhandled RPC op: ${body.op}`);
|
|
30763
30851
|
}
|
|
@@ -39484,7 +39572,7 @@ function parseSheetData(worksheet, styles, sharedStrings, hyperlinkMap, defaultR
|
|
|
39484
39572
|
if (outlineLevel > 0) {
|
|
39485
39573
|
rowOutlineLevels.set(rowIndex, outlineLevel);
|
|
39486
39574
|
}
|
|
39487
|
-
|
|
39575
|
+
const rowHidden = rowEl["@_hidden"] === "1" || rowEl["@_hidden"] === "true";
|
|
39488
39576
|
rowCount++;
|
|
39489
39577
|
if (rowCount > maxRows) continue;
|
|
39490
39578
|
const height = rowEl["@_ht"] ? parseFloat(rowEl["@_ht"]) : defaultRowHeight;
|
|
@@ -39496,7 +39584,7 @@ function parseSheetData(worksheet, styles, sharedStrings, hyperlinkMap, defaultR
|
|
|
39496
39584
|
if (parsed) {
|
|
39497
39585
|
cells.push(parsed);
|
|
39498
39586
|
if (parsed.column > maxCol) maxCol = parsed.column;
|
|
39499
|
-
if (rowCount <= 200) {
|
|
39587
|
+
if (rowCount <= 200 && !rowHidden) {
|
|
39500
39588
|
const currentMax = maxContentWidth.get(parsed.column) ?? 0;
|
|
39501
39589
|
if (parsed.value.length > currentMax) {
|
|
39502
39590
|
maxContentWidth.set(parsed.column, parsed.value.length);
|
|
@@ -39509,7 +39597,7 @@ function parseSheetData(worksheet, styles, sharedStrings, hyperlinkMap, defaultR
|
|
|
39509
39597
|
index: rowIndex,
|
|
39510
39598
|
height,
|
|
39511
39599
|
cells,
|
|
39512
|
-
hidden:
|
|
39600
|
+
hidden: rowHidden
|
|
39513
39601
|
});
|
|
39514
39602
|
}
|
|
39515
39603
|
return {
|
|
@@ -39,7 +39,7 @@ export interface StarterFile {
|
|
|
39
39
|
* scaffolds resolve the live version via `fetchLatestNpmVersion` and only
|
|
40
40
|
* fall back here when the lookup fails.
|
|
41
41
|
*/
|
|
42
|
-
export declare const STARTER_FALLBACK_UI_VERSION = "
|
|
42
|
+
export declare const STARTER_FALLBACK_UI_VERSION = "2.3.0";
|
|
43
43
|
export declare const STARTER_FALLBACK_SDK_VERSION = "0.11.0";
|
|
44
44
|
/**
|
|
45
45
|
* react-native pin for scaffolded apps. Matches the monorepo frontend's pin so
|
package/dist/starter_template.js
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* scaffolds resolve the live version via `fetchLatestNpmVersion` and only
|
|
36
36
|
* fall back here when the lookup fails.
|
|
37
37
|
*/
|
|
38
|
-
export const STARTER_FALLBACK_UI_VERSION = "
|
|
38
|
+
export const STARTER_FALLBACK_UI_VERSION = "2.3.0";
|
|
39
39
|
export const STARTER_FALLBACK_SDK_VERSION = "0.11.0";
|
|
40
40
|
/**
|
|
41
41
|
* react-native pin for scaffolded apps. Matches the monorepo frontend's pin so
|