@arcote.tech/arc 0.3.5 → 0.3.6
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/adapters/command-wire.d.ts +10 -0
- package/dist/adapters/event-wire.d.ts +1 -0
- package/dist/context-element/tree/index.d.ts +9 -0
- package/dist/context-element/tree/tree-context.d.ts +98 -0
- package/dist/context-element/tree/tree-data.d.ts +43 -0
- package/dist/context-element/tree/tree.d.ts +142 -0
- package/dist/index.js +77 -17
- package/dist/observability/index.d.ts +4 -0
- package/dist/observability/telemetry-browser.d.ts +6 -0
- package/dist/observability/telemetry-server.d.ts +6 -0
- package/dist/observability/telemetry.d.ts +51 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Extends Wire to provide command-specific functionality:
|
|
5
5
|
* - Execute commands remotely on the server
|
|
6
6
|
* - Handle command request/response serialization
|
|
7
|
+
* - Automatic FormData handling for file uploads
|
|
7
8
|
*/
|
|
8
9
|
import { Wire } from "./wire";
|
|
9
10
|
export declare class CommandWire extends Wire {
|
|
@@ -16,6 +17,15 @@ export declare class CommandWire extends Wire {
|
|
|
16
17
|
* @returns Command result from server
|
|
17
18
|
*/
|
|
18
19
|
executeCommand(name: string, params: any): Promise<any>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if object contains any File instances (recursively)
|
|
22
|
+
*/
|
|
23
|
+
private containsFile;
|
|
24
|
+
/**
|
|
25
|
+
* Convert params object to FormData for file uploads
|
|
26
|
+
* Files are appended directly, other values are JSON-stringified
|
|
27
|
+
*/
|
|
28
|
+
private toFormData;
|
|
19
29
|
/**
|
|
20
30
|
* Register a command handler on the server side
|
|
21
31
|
* Called during init() for server environment
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { ArcObjectAny } from "../../elements/object";
|
|
2
|
+
import type { $type } from "../../utils/types/get-type";
|
|
3
|
+
import type { ArcTreeData } from "./tree-data";
|
|
4
|
+
/**
|
|
5
|
+
* Tree node as returned from queries
|
|
6
|
+
* Includes node data plus tree metadata
|
|
7
|
+
*/
|
|
8
|
+
export interface TreeNode<NodeTypes extends Record<string, ArcObjectAny> = Record<string, ArcObjectAny>, T extends keyof NodeTypes = keyof NodeTypes> {
|
|
9
|
+
_id: string;
|
|
10
|
+
type: T;
|
|
11
|
+
parentId: string | null;
|
|
12
|
+
order: number;
|
|
13
|
+
treeId: string;
|
|
14
|
+
data: T extends keyof NodeTypes ? $type<NodeTypes[T]> : never;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Tree edge record
|
|
18
|
+
*/
|
|
19
|
+
export interface TreeEdge {
|
|
20
|
+
_id: string;
|
|
21
|
+
childId: string;
|
|
22
|
+
parentId: string | null;
|
|
23
|
+
childType: string;
|
|
24
|
+
order: number;
|
|
25
|
+
treeId: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Options for tree find queries
|
|
29
|
+
*/
|
|
30
|
+
export interface TreeFindOptions<NodeTypes extends Record<string, ArcObjectAny>> {
|
|
31
|
+
/** Filter by specific node types */
|
|
32
|
+
types?: (keyof NodeTypes)[];
|
|
33
|
+
/** Filter by parent ID (null for root nodes) */
|
|
34
|
+
parentId?: string | null;
|
|
35
|
+
/** Filter by tree ID (for multi-tenant) */
|
|
36
|
+
treeId?: string;
|
|
37
|
+
/** Sort order */
|
|
38
|
+
orderBy?: {
|
|
39
|
+
order: "asc" | "desc";
|
|
40
|
+
};
|
|
41
|
+
/** Limit results */
|
|
42
|
+
limit?: number;
|
|
43
|
+
/** Recursive depth (for findDescendants) */
|
|
44
|
+
depth?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Query context for reading tree data
|
|
48
|
+
*/
|
|
49
|
+
export interface TreeQueryContext<Data extends ArcTreeData> {
|
|
50
|
+
/** Find nodes with optional filtering */
|
|
51
|
+
find(options?: TreeFindOptions<Data["nodeTypes"]>): Promise<TreeNode<Data["nodeTypes"]>[]>;
|
|
52
|
+
/** Find single node by ID */
|
|
53
|
+
findOne(id: string): Promise<TreeNode<Data["nodeTypes"]> | undefined>;
|
|
54
|
+
/** Find direct children of a node */
|
|
55
|
+
findChildren(parentId: string | null, options?: {
|
|
56
|
+
types?: (keyof Data["nodeTypes"])[];
|
|
57
|
+
treeId?: string;
|
|
58
|
+
}): Promise<TreeNode<Data["nodeTypes"]>[]>;
|
|
59
|
+
/** Find ancestors (path to root) */
|
|
60
|
+
findAncestors(nodeId: string): Promise<TreeNode<Data["nodeTypes"]>[]>;
|
|
61
|
+
/** Find descendants recursively */
|
|
62
|
+
findDescendants(nodeId: string, depth?: number): Promise<TreeNode<Data["nodeTypes"]>[]>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Mutation context for modifying tree data
|
|
66
|
+
*/
|
|
67
|
+
export interface TreeMutationContext<Data extends ArcTreeData> {
|
|
68
|
+
/** Create a new node */
|
|
69
|
+
create<T extends keyof Data["nodeTypes"]>(type: T, data: $type<Data["nodeTypes"][T]>, options?: {
|
|
70
|
+
parentId?: string | null;
|
|
71
|
+
order?: number;
|
|
72
|
+
treeId?: string;
|
|
73
|
+
}): Promise<string>;
|
|
74
|
+
/** Update node data */
|
|
75
|
+
update<T extends keyof Data["nodeTypes"]>(id: string, data: Partial<$type<Data["nodeTypes"][T]>>): Promise<void>;
|
|
76
|
+
/** Delete node (with cascade) */
|
|
77
|
+
delete(id: string): Promise<void>;
|
|
78
|
+
/** Move node to new parent/position */
|
|
79
|
+
move(id: string, options: {
|
|
80
|
+
parentId?: string | null;
|
|
81
|
+
order?: number;
|
|
82
|
+
}): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Serialized tree format for export/import
|
|
86
|
+
*/
|
|
87
|
+
export interface SerializedTree<Data extends ArcTreeData> {
|
|
88
|
+
name: string;
|
|
89
|
+
treeId: string;
|
|
90
|
+
nodes: {
|
|
91
|
+
[K in keyof Data["nodeTypes"]]?: Array<{
|
|
92
|
+
_id: string;
|
|
93
|
+
data: $type<Data["nodeTypes"][K]>;
|
|
94
|
+
}>;
|
|
95
|
+
};
|
|
96
|
+
edges: TreeEdge[];
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=tree-context.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ArcObjectAny } from "../../elements/object";
|
|
2
|
+
import type { ArcTokenAny } from "../../token/token";
|
|
3
|
+
/**
|
|
4
|
+
* Protection function for tree access control
|
|
5
|
+
* Returns read/write conditions based on token params
|
|
6
|
+
*/
|
|
7
|
+
export type TreeProtectionFn<T extends ArcTokenAny> = (params: T extends {
|
|
8
|
+
params: infer P;
|
|
9
|
+
} ? P : never) => {
|
|
10
|
+
read?: Record<string, any> | false;
|
|
11
|
+
write?: Record<string, any> | false;
|
|
12
|
+
} | false;
|
|
13
|
+
/**
|
|
14
|
+
* Tree protection configuration
|
|
15
|
+
*/
|
|
16
|
+
export interface TreeProtection<T extends ArcTokenAny = ArcTokenAny> {
|
|
17
|
+
token: T;
|
|
18
|
+
protectionFn: TreeProtectionFn<T>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Node type definition within a tree
|
|
22
|
+
*/
|
|
23
|
+
export interface TreeNodeTypeDefinition<Name extends string = string, Schema extends ArcObjectAny = ArcObjectAny> {
|
|
24
|
+
name: Name;
|
|
25
|
+
schema: Schema;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Data structure for ArcTree configuration
|
|
29
|
+
* Follows Arc's factory pattern with immutable data
|
|
30
|
+
*/
|
|
31
|
+
export interface ArcTreeData {
|
|
32
|
+
/** Unique tree name */
|
|
33
|
+
name: string;
|
|
34
|
+
/** Optional description for documentation */
|
|
35
|
+
description?: string;
|
|
36
|
+
/** Map of node type names to their schemas */
|
|
37
|
+
nodeTypes: Record<string, ArcObjectAny>;
|
|
38
|
+
/** Token-based protections */
|
|
39
|
+
protections: TreeProtection[];
|
|
40
|
+
/** Tree ID field name for multi-tenant support */
|
|
41
|
+
treeIdField?: string;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=tree-data.d.ts.map
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { DatabaseStoreSchema } from "../../data-storage/database-store";
|
|
2
|
+
import { ArcObject, type ArcObjectAny, type ArcRawShape } from "../../elements/object";
|
|
3
|
+
import type { ModelAdapters } from "../../model/model-adapters";
|
|
4
|
+
import type { ArcTokenAny } from "../../token/token";
|
|
5
|
+
import type { Merge } from "../../utils";
|
|
6
|
+
import { ArcContextElement } from "../context-element";
|
|
7
|
+
import type { SerializedTree, TreeMutationContext, TreeQueryContext } from "./tree-context";
|
|
8
|
+
import type { ArcTreeData, TreeProtectionFn } from "./tree-data";
|
|
9
|
+
/**
|
|
10
|
+
* Arc Tree - Hierarchical data structure with multiple node types
|
|
11
|
+
*
|
|
12
|
+
* Trees enable defining hierarchical data where each node type has its own
|
|
13
|
+
* dedicated view/table. The tree provides a unified query interface that
|
|
14
|
+
* joins these views based on parent-child relationships.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const contentTree = tree("contentTree")
|
|
19
|
+
* .nodeType("texts", { text: string() })
|
|
20
|
+
* .nodeType("brollIdeas", { idea: string() })
|
|
21
|
+
* .nodeType("graphicIdeas", { idea: string() });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare class ArcTree<const Data extends ArcTreeData> extends ArcContextElement<Data["name"]> {
|
|
25
|
+
private readonly data;
|
|
26
|
+
constructor(data: Data);
|
|
27
|
+
/**
|
|
28
|
+
* Get tree name
|
|
29
|
+
*/
|
|
30
|
+
get treeName(): Data["name"];
|
|
31
|
+
/**
|
|
32
|
+
* Get node types configuration
|
|
33
|
+
*/
|
|
34
|
+
get nodeTypes(): Data["nodeTypes"];
|
|
35
|
+
/**
|
|
36
|
+
* Set tree description for documentation
|
|
37
|
+
*/
|
|
38
|
+
description<const Desc extends string>(description: Desc): ArcTree<Merge<Data, {
|
|
39
|
+
description: Desc;
|
|
40
|
+
}>>;
|
|
41
|
+
/**
|
|
42
|
+
* Define a node type with its schema
|
|
43
|
+
* Each node type gets its own dedicated view/table
|
|
44
|
+
*
|
|
45
|
+
* @param typeName - Unique name for this node type within the tree
|
|
46
|
+
* @param schema - Schema defining the node's data fields
|
|
47
|
+
*/
|
|
48
|
+
nodeType<TypeName extends string, Schema extends ArcRawShape | ArcObjectAny>(typeName: TypeName, schema: Schema): ArcTree<Merge<Data, {
|
|
49
|
+
nodeTypes: Data["nodeTypes"] & { [K in TypeName]: ArcObject<ArcRawShape, [{
|
|
50
|
+
name: "type";
|
|
51
|
+
validator: (value: any) => false | {
|
|
52
|
+
current: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function";
|
|
53
|
+
expected: "object";
|
|
54
|
+
};
|
|
55
|
+
}, {
|
|
56
|
+
name: "schema";
|
|
57
|
+
validator: (value: any) => false | {
|
|
58
|
+
[x: string]: unknown;
|
|
59
|
+
};
|
|
60
|
+
}]> | (Schema & ArcObject<any, any>); };
|
|
61
|
+
}>>;
|
|
62
|
+
/**
|
|
63
|
+
* Add token-based protection to this tree
|
|
64
|
+
* Defines read/write access conditions based on token params
|
|
65
|
+
*/
|
|
66
|
+
protectBy<T extends ArcTokenAny>(token: T, protectionFn: TreeProtectionFn<T>): ArcTree<Data>;
|
|
67
|
+
/**
|
|
68
|
+
* Check if tree has protections
|
|
69
|
+
*/
|
|
70
|
+
get hasProtections(): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Get all protection configurations
|
|
73
|
+
*/
|
|
74
|
+
get protections(): import("./tree-data").TreeProtection<ArcTokenAny>[];
|
|
75
|
+
/**
|
|
76
|
+
* Generate all context elements (events, views) for this tree
|
|
77
|
+
* These should be registered in the Arc context
|
|
78
|
+
*/
|
|
79
|
+
getElements(): ArcContextElement<any>[];
|
|
80
|
+
/**
|
|
81
|
+
* Generate query context for this tree
|
|
82
|
+
* Provides read access to tree data
|
|
83
|
+
*/
|
|
84
|
+
queryContext(adapters: ModelAdapters): TreeQueryContext<Data>;
|
|
85
|
+
/**
|
|
86
|
+
* Generate mutation context for this tree
|
|
87
|
+
* Provides write access to tree data
|
|
88
|
+
*/
|
|
89
|
+
mutateContext(adapters: ModelAdapters): TreeMutationContext<Data>;
|
|
90
|
+
/**
|
|
91
|
+
* Generate database store schema for this tree
|
|
92
|
+
* Returns schemas for all node type views + edges view
|
|
93
|
+
*/
|
|
94
|
+
databaseStoreSchema(): DatabaseStoreSchema;
|
|
95
|
+
/**
|
|
96
|
+
* Serialize tree data to JSON format
|
|
97
|
+
* Exports nodes grouped by type and edges array
|
|
98
|
+
*
|
|
99
|
+
* @param adapters - Model adapters for data access
|
|
100
|
+
* @param treeId - Tree ID to serialize (defaults to "default")
|
|
101
|
+
*/
|
|
102
|
+
serialize(adapters: ModelAdapters, treeId?: string): Promise<SerializedTree<Data>>;
|
|
103
|
+
/**
|
|
104
|
+
* Serialize a subtree starting from a specific node
|
|
105
|
+
* Includes only the specified node and its descendants
|
|
106
|
+
*
|
|
107
|
+
* @param adapters - Model adapters for data access
|
|
108
|
+
* @param nodeId - Root node ID of the subtree
|
|
109
|
+
*/
|
|
110
|
+
serializeSubtree(adapters: ModelAdapters, nodeId: string): Promise<SerializedTree<Data>>;
|
|
111
|
+
/**
|
|
112
|
+
* Deserialize tree data from JSON format
|
|
113
|
+
* Validates node data against schemas and reconstructs relationships
|
|
114
|
+
*
|
|
115
|
+
* @param adapters - Model adapters for data access
|
|
116
|
+
* @param serialized - Serialized tree data
|
|
117
|
+
*/
|
|
118
|
+
deserialize(adapters: ModelAdapters, serialized: SerializedTree<Data>): Promise<void>;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Create a new tree with the given name
|
|
122
|
+
*
|
|
123
|
+
* @param name - Unique tree name
|
|
124
|
+
* @returns New tree instance ready for configuration
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const contentTree = tree("contentTree")
|
|
129
|
+
* .nodeType("texts", { text: string() })
|
|
130
|
+
* .nodeType("brollIdeas", { idea: string() });
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export declare function tree<const Name extends string>(name: Name): ArcTree<{
|
|
134
|
+
name: Name;
|
|
135
|
+
nodeTypes: {};
|
|
136
|
+
protections: [];
|
|
137
|
+
}>;
|
|
138
|
+
/**
|
|
139
|
+
* Type alias for any tree (used in collections)
|
|
140
|
+
*/
|
|
141
|
+
export type ArcTreeAny = ArcTree<any>;
|
|
142
|
+
//# sourceMappingURL=tree.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -84,9 +84,19 @@ class CommandWire extends Wire {
|
|
|
84
84
|
super(baseUrl);
|
|
85
85
|
}
|
|
86
86
|
async executeCommand(name, params) {
|
|
87
|
+
const hasFile = this.containsFile(params);
|
|
88
|
+
let body;
|
|
89
|
+
let headers;
|
|
90
|
+
if (hasFile) {
|
|
91
|
+
body = this.toFormData(params);
|
|
92
|
+
} else {
|
|
93
|
+
body = JSON.stringify(params);
|
|
94
|
+
headers = { "Content-Type": "application/json" };
|
|
95
|
+
}
|
|
87
96
|
const response = await this.fetch(`/command/${name}`, {
|
|
88
97
|
method: "POST",
|
|
89
|
-
body
|
|
98
|
+
body,
|
|
99
|
+
headers
|
|
90
100
|
});
|
|
91
101
|
if (!response.ok) {
|
|
92
102
|
const errorText = await response.text();
|
|
@@ -94,10 +104,35 @@ class CommandWire extends Wire {
|
|
|
94
104
|
}
|
|
95
105
|
return await response.json();
|
|
96
106
|
}
|
|
107
|
+
containsFile(obj) {
|
|
108
|
+
if (obj instanceof File || obj instanceof Blob)
|
|
109
|
+
return true;
|
|
110
|
+
if (obj === null || typeof obj !== "object")
|
|
111
|
+
return false;
|
|
112
|
+
if (Array.isArray(obj))
|
|
113
|
+
return obj.some((v) => this.containsFile(v));
|
|
114
|
+
return Object.values(obj).some((v) => this.containsFile(v));
|
|
115
|
+
}
|
|
116
|
+
toFormData(params) {
|
|
117
|
+
const formData = new FormData;
|
|
118
|
+
for (const [key, value] of Object.entries(params)) {
|
|
119
|
+
if (value instanceof File) {
|
|
120
|
+
formData.append(key, value, value.name);
|
|
121
|
+
} else if (value instanceof Blob) {
|
|
122
|
+
formData.append(key, value);
|
|
123
|
+
} else {
|
|
124
|
+
formData.append(key, JSON.stringify(value));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return formData;
|
|
128
|
+
}
|
|
97
129
|
}
|
|
98
130
|
// src/adapters/event-wire.ts
|
|
131
|
+
var eventWireInstanceCounter = 0;
|
|
132
|
+
|
|
99
133
|
class EventWire {
|
|
100
134
|
baseUrl;
|
|
135
|
+
instanceId;
|
|
101
136
|
ws = null;
|
|
102
137
|
state = "disconnected";
|
|
103
138
|
token = null;
|
|
@@ -109,6 +144,7 @@ class EventWire {
|
|
|
109
144
|
syncRequested = false;
|
|
110
145
|
constructor(baseUrl) {
|
|
111
146
|
this.baseUrl = baseUrl;
|
|
147
|
+
this.instanceId = ++eventWireInstanceCounter;
|
|
112
148
|
}
|
|
113
149
|
setAuthToken(token) {
|
|
114
150
|
this.token = token;
|
|
@@ -127,16 +163,26 @@ class EventWire {
|
|
|
127
163
|
if (this.state !== "disconnected")
|
|
128
164
|
return;
|
|
129
165
|
this.state = "connecting";
|
|
130
|
-
|
|
166
|
+
let wsUrl;
|
|
167
|
+
if (this.baseUrl.startsWith("http://") || this.baseUrl.startsWith("https://")) {
|
|
168
|
+
wsUrl = this.baseUrl.replace(/^http/, "ws");
|
|
169
|
+
} else {
|
|
170
|
+
const protocol = typeof window !== "undefined" && window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
171
|
+
const host = typeof window !== "undefined" ? window.location.host : "localhost";
|
|
172
|
+
wsUrl = `${protocol}//${host}${this.baseUrl}`;
|
|
173
|
+
}
|
|
131
174
|
const tokenParam = this.token ? `?token=${this.token}` : "";
|
|
132
175
|
const url = `${wsUrl}/ws${tokenParam}`;
|
|
133
176
|
try {
|
|
134
177
|
this.ws = new WebSocket(url);
|
|
135
178
|
this.ws.onopen = () => {
|
|
136
|
-
this.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
179
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
180
|
+
this.state = "connected";
|
|
181
|
+
this.requestSync();
|
|
182
|
+
this.flushPendingEvents();
|
|
183
|
+
} else {
|
|
184
|
+
console.log(`[EventWire] onopen called but ws is not OPEN, readyState:`, this.ws?.readyState);
|
|
185
|
+
}
|
|
140
186
|
};
|
|
141
187
|
this.ws.onmessage = (event) => {
|
|
142
188
|
try {
|
|
@@ -146,7 +192,7 @@ class EventWire {
|
|
|
146
192
|
console.error("EventWire: Failed to parse message", err);
|
|
147
193
|
}
|
|
148
194
|
};
|
|
149
|
-
this.ws.onclose = () => {
|
|
195
|
+
this.ws.onclose = (event) => {
|
|
150
196
|
this.state = "disconnected";
|
|
151
197
|
this.ws = null;
|
|
152
198
|
console.log("EventWire disconnected");
|
|
@@ -154,9 +200,14 @@ class EventWire {
|
|
|
154
200
|
};
|
|
155
201
|
this.ws.onerror = (err) => {
|
|
156
202
|
console.error("EventWire error:", err);
|
|
203
|
+
if (this.state === "connecting") {
|
|
204
|
+
this.state = "disconnected";
|
|
205
|
+
this.ws = null;
|
|
206
|
+
}
|
|
157
207
|
};
|
|
158
208
|
} catch (err) {
|
|
159
209
|
this.state = "disconnected";
|
|
210
|
+
this.ws = null;
|
|
160
211
|
console.error("EventWire: Failed to connect", err);
|
|
161
212
|
this.scheduleReconnect();
|
|
162
213
|
}
|
|
@@ -174,19 +225,28 @@ class EventWire {
|
|
|
174
225
|
this.syncRequested = false;
|
|
175
226
|
}
|
|
176
227
|
syncEvents(events) {
|
|
177
|
-
|
|
228
|
+
const isActuallyConnected = this.ws && this.ws.readyState === WebSocket.OPEN;
|
|
229
|
+
if (!isActuallyConnected) {
|
|
230
|
+
if (this.state === "connected" && !this.ws) {
|
|
231
|
+
this.state = "disconnected";
|
|
232
|
+
}
|
|
178
233
|
this.pendingEvents.push(...events);
|
|
234
|
+
if (this.state === "disconnected") {
|
|
235
|
+
this.connect();
|
|
236
|
+
}
|
|
179
237
|
return;
|
|
180
238
|
}
|
|
181
|
-
this.ws
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
239
|
+
if (this.ws) {
|
|
240
|
+
this.ws.send(JSON.stringify({
|
|
241
|
+
type: "sync-events",
|
|
242
|
+
events: events.map((e) => ({
|
|
243
|
+
localId: e.localId,
|
|
244
|
+
type: e.type,
|
|
245
|
+
payload: e.payload,
|
|
246
|
+
createdAt: e.createdAt
|
|
247
|
+
}))
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
190
250
|
}
|
|
191
251
|
onEvent(callback) {
|
|
192
252
|
this.onEventCallback = callback;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ArcTelemetry, type TelemetryConfig } from "./telemetry";
|
|
2
|
+
/**
|
|
3
|
+
* Initialize OpenTelemetry for Node.js/Bun server environment
|
|
4
|
+
*/
|
|
5
|
+
export declare function initServerTelemetry(config: TelemetryConfig): ArcTelemetry;
|
|
6
|
+
//# sourceMappingURL=telemetry-server.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type Span } from "@opentelemetry/api";
|
|
2
|
+
export type ObservabilityMode = "development" | "production" | "disabled";
|
|
3
|
+
export interface TelemetryConfig {
|
|
4
|
+
serviceName: string;
|
|
5
|
+
environment: "client" | "server";
|
|
6
|
+
endpoint?: string;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
sampleRate?: number;
|
|
9
|
+
mode?: ObservabilityMode;
|
|
10
|
+
includePayloads?: boolean;
|
|
11
|
+
debug?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Arc Telemetry - Wrapper around OpenTelemetry for Arc Framework
|
|
15
|
+
*
|
|
16
|
+
* Provides simplified API for tracing commands, events, and views
|
|
17
|
+
*/
|
|
18
|
+
export declare class ArcTelemetry {
|
|
19
|
+
private tracer;
|
|
20
|
+
readonly config: TelemetryConfig;
|
|
21
|
+
constructor(config: TelemetryConfig);
|
|
22
|
+
/**
|
|
23
|
+
* Set the tracer instance (called by init functions)
|
|
24
|
+
*/
|
|
25
|
+
setTracer(tracer: any): void;
|
|
26
|
+
/**
|
|
27
|
+
* Start a span with automatic error handling
|
|
28
|
+
*/
|
|
29
|
+
startSpan<T>(name: string, fn: (span: Span) => Promise<T>, attributes?: Record<string, any>): Promise<T>;
|
|
30
|
+
/**
|
|
31
|
+
* Get current active span
|
|
32
|
+
*/
|
|
33
|
+
getCurrentSpan(): Span | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Add attributes to current span
|
|
36
|
+
*/
|
|
37
|
+
addAttributes(attributes: Record<string, any>): void;
|
|
38
|
+
/**
|
|
39
|
+
* Create a child span (must be manually ended)
|
|
40
|
+
*/
|
|
41
|
+
createChildSpan(name: string, attributes?: Record<string, any>): Span | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Serialize value for span attributes (handles objects, arrays, etc)
|
|
44
|
+
*/
|
|
45
|
+
private serializeValue;
|
|
46
|
+
/**
|
|
47
|
+
* Check if payloads should be included based on mode
|
|
48
|
+
*/
|
|
49
|
+
shouldIncludePayloads(): boolean;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=telemetry.d.ts.map
|
package/package.json
CHANGED