-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathLanguage.ts
More file actions
254 lines (210 loc) · 8.59 KB
/
Language.ts
File metadata and controls
254 lines (210 loc) · 8.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import type { Address } from '../Address'
import { DID } from '../DID';
import type { Expression } from '../expression/Expression'
import { Perspective, PerspectiveExpression } from '../perspectives/Perspective';
import { PerspectiveDiff } from '../perspectives/PerspectiveDiff';
import { InputType, Field, ObjectType } from "type-graphql";
/** Interface of AD4M Languages
*
* Any JavaScript module that implements a create() function that returns an object that implements this interface
* is a valid AD4M language.
* So the AD4M-internal representation of a language is an object that implements this interface.
*
* Since there are a few different kinds of languages, this interface is split into optional sub-interfaces.
* The only required property is the name of the language.
*
* The most usual kind of language is the "Expression Language", which is a language that can be used to create
* and share Expressions.
* For that, implement the expressionsAdapter and expressionUI interface.
*
* The second most common kind of language is the "Link Language", which is a language that builds the core
* of AD4M Neighbourhoods.
* For that, implement the linksAdapter interface.
*/
export interface Language {
readonly name: string;
/** Flagging expressions as immutable to enable
* expression caching in the ad4m-executor
*/
isImmutableExpression?(expression: Address): boolean;
// Adapter implementations:
/** ExpressionAdapter implements means of getting an Expression
* by address and putting an expression
*/
readonly expressionAdapter?: ExpressionAdapter;
/** Interface for getting UI/web components for rendering Expressions of this Language */
readonly expressionUI?: ExpressionUI;
/** Interface of LinkLanguages for the core implementation of Neighbourhoods */
readonly linksAdapter?: LinkSyncAdapter;
/** Implementation of a Language that defines and stores Languages*/
readonly languageAdapter?: LanguageAdapter;
/** Optional adapter for getting Expressions by author */
readonly getByAuthorAdapter?: GetByAuthorAdapter;
/** Optional adapter for getting all Expressions */
readonly getAllAdapter?: GetAllAdapter;
/** Optional adapter for direct messaging between agents */
readonly directMessageAdapter?: DirectMessageAdapter;
/** Interface for providing UI components for the settings of this Language */
readonly settingsUI?: SettingsUI;
/** Optional function to make any cleanup/teardown if your language gets deleting in the ad4m-executor */
readonly teardown?: () => void;
/** All available interactions this agent could execute on given expression */
interactions(expression: Address): Interaction[];
}
/** UI factories returning web components */
export interface ExpressionUI {
/** Returns JS code of a web component that renders the given expression */
icon(): string;
/** Returns JS code of a web component used to create new expressions */
constructorIcon(): string;
}
export interface SettingsUI {
settingsIcon(): string;
}
/** Interface for the most common Expression Languages */
export interface ExpressionAdapter {
/** Returns an Expression by address, or null if there is no Expression
* with that given address
*/
get(address: Address): Promise<Expression | null>;
/** Strategy for putting an expression with needs to be different
* for those two cases:
* 1. PublicSharing means that this language supports the creation
* and sharing of Expressions, which is the common use-case
* 2. ReadOnlyLanguage means that the Language implements a pre-defined
* set of expressions (which can be infinite or finite).
* For example the url-iframe Language which directly maps URLs to
* addresses - meaning every well formed URL is an address in this
* Language. Or a potential Language implementing the verbs/predicates
* of a spec like FOAF.
*/
putAdapter: PublicSharing | ReadOnlyLanguage;
}
/** Implement this interface if your Language supports creation of sharing
* of Expressions.
* See ExpressionAdapter
*/
export interface PublicSharing {
/** Create an Expression and shares it.
* Return the Expression's address.
* @param content is the object created by the constructorIcon component
*/
createPublic(content: object): Promise<Address>;
}
/** Implement this interface if your Language is defined over a static
* set of pre-defined Expressions.
*/
export interface ReadOnlyLanguage {
/** This just calculates the address of an object
* @param content is the object created by the constructorIcon component
*/
addressOf(content: object): Promise<Address>;
}
export interface LanguageAdapter {
getLanguageSource(address: Address): Promise<string>;
}
// Implement this if your Language supports retrieval of all Expressions
// authored by a given agent
export interface GetByAuthorAdapter {
/// Get expressions authored by a given Agent/Identity
getByAuthor(author: DID, count: number, page: number): Promise<Expression[] | null>;
}
// Implement this if your Language supports retrieval of all Expressions
// stored in the space of that Language.
// Might not be trivial (without trade-off) for Holochain or DHTs
// in general - hence not a required interface.
export interface GetAllAdapter {
/// Get expressions authored by a given Agent/Identity
getAll(filter: any, count: number, page: number): Promise<Expression[] | null>;
}
export type PerspectiveDiffObserver = (diff: PerspectiveDiff)=>void;
/** Interface for "Link Languages" that facilitate the synchronization
* between agents' local Perspectives inside a Neighbourhood.
* The assumption is that every version of the shared Perspective
* is labeled with a unique revision string.
* Changes are committed and retrieved through diffs.
* Think of a LinkSyncAdapter as a git branch to which agents commit
* their changes to and pull diffs from their current revision
* to the latest one.
*/
export interface LinkSyncAdapter {
writable(): boolean;
public(): boolean;
others(): Promise<DID[]>;
/** Call this to check if there are new changes
* (compare returned revision with last one that was pulled)
*/
latestRevision(): Promise<string>;
/** What revision are we on now -> what changes are included in output of render() */
currentRevision(): Promise<string>;
/** Check for and get new changes */
pull(): Promise<PerspectiveDiff>;
/** Returns the full, rendered Perspective at currentRevision */
render(): Promise<Perspective>;
/** Publish changes */
commit(diff: PerspectiveDiff): Promise<string>;
/** Get push notification when a diff got published */
addCallback(callback: PerspectiveDiffObserver);
}
export type MessageCallback = (message: PerspectiveExpression) => void;
export type StatusCallback = (caller: DID) => Perspective;
export interface DirectMessageAdapter {
recipient(): DID;
status(): Promise<PerspectiveExpression | void>;
sendP2P(message: Perspective): Promise<PerspectiveExpression|void>;
sendInbox(message: Perspective): Promise<PerspectiveExpression|void>;
setStatus(status: PerspectiveExpression);
inbox(filter?: string): Promise<PerspectiveExpression[]>
addMessageCallback(callback: MessageCallback);
}
@ObjectType()
export class InteractionParameter {
@Field()
name: string
@Field()
type: string
}
@ObjectType()
export class InteractionMeta {
@Field()
label: string;
@Field()
name: string;
@Field(type => [InteractionParameter])
parameters: InteractionParameter[]
}
export interface Interaction {
readonly label: string;
readonly name: string;
readonly parameters: InteractionParameter[];
execute(parameters: object): Promise<string|null>;
}
@InputType()
export class InteractionCall {
@Field()
name: string;
@Field()
parametersStringified: string;
public get parameters(): object {
return JSON.parse(this.parametersStringified)
}
constructor(name: string, parameters: object) {
this.name = name
this.parametersStringified = JSON.stringify(parameters)
}
}
export class OnlineAgent {
did: DID
status: string
}
export class TelepresenceRpcCall {
fn_name: string
params: object
}
export type TelepresenceRpcCallback = (call: TelepresenceRpcCall) => object;
export interface TelepresenceAdapter {
setOnlineStatus(status: string);
getOnlineAgents(): [OnlineAgent];
rpcCall(remoteAgentDid: string, call: TelepresenceRpcCall): object;
registerRpcCallback(callback: TelepresenceRpcCall);
}