@rivetkit/rivetkit-wasm 0.0.0-main.879b6a2 → 0.0.0-main.b2d3b94

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/index.d.ts CHANGED
@@ -1,175 +1,2 @@
1
- export function start(): void;
2
- export function awaitPromise(promise: Promise<any>): Promise<any>;
3
- export function uint8ArrayFromBytes(bytes: Uint8Array): Uint8Array;
4
- export function bridgeRivetErrorPrefix(): string;
5
- export function roundTripBytes(bytes: Uint8Array): Uint8Array;
6
-
7
- export class ActorContext {
8
- free(): void;
9
- keepAwake(promise: Promise<any>): void;
10
- saveState(payload: any): Promise<void>;
11
- waitUntil(promise: Promise<any>): void;
12
- abortSignal(): any;
13
- connectConn(params: Uint8Array, request: any): Promise<ConnHandle>;
14
- requestSave(opts: any): void;
15
- registerTask(promise: Promise<any>): void;
16
- runtimeState(): any;
17
- endKeepAwake(region_id: number): void;
18
- beginKeepAwake(): number;
19
- inspectorSnapshot(): object;
20
- endOnStateChange(): void;
21
- restartRunHandler(): void;
22
- beginOnStateChange(): void;
23
- requestSaveAndWait(opts: any): Promise<void>;
24
- verifyInspectorAuth(bearer_token?: string | null): Promise<void>;
25
- endWebsocketCallback(region_id: number): void;
26
- beginWebsocketCallback(): number;
27
- dirtyHibernatableConns(): Array<any>;
28
- kv(): Kv;
29
- waitForTrackedShutdownWork(): Promise<boolean>;
30
- takePendingHibernationChanges(): Array<any>;
31
- key(): any;
32
- constructor();
33
- sql(): SqliteDb;
34
- name(): string;
35
- conns(): Array<any>;
36
- queue(): Queue;
37
- sleep(): void;
38
- state(): Uint8Array;
39
- region(): string;
40
- destroy(): void;
41
- actorId(): string;
42
- schedule(): Schedule;
43
- broadcast(name: string, args: Uint8Array): void;
44
- setAlarm(timestamp_ms?: number | null): void;
45
- }
46
-
47
- export class ActorFactory {
48
- free(): void;
49
- constructor(callbacks: any, config: any);
50
- }
51
-
52
- export class CancellationToken {
53
- free(): void;
54
- onCancelled(callback: Function): void;
55
- constructor();
56
- cancel(): void;
57
- aborted(): boolean;
58
- }
59
-
60
- export class ConnHandle {
61
- private constructor();
62
- free(): void;
63
- disconnect(reason?: string | null): Promise<void>;
64
- isHibernatable(): boolean;
65
- id(): string;
66
- send(name: string, args: Uint8Array): void;
67
- state(): Uint8Array;
68
- params(): Uint8Array;
69
- setState(state: Uint8Array): void;
70
- }
71
-
72
- export class CoreRegistry {
73
- free(): void;
74
- handleServerlessRequest(
75
- req: any,
76
- on_stream_event: Function,
77
- cancel_token: CancellationToken,
78
- config: any,
79
- ): Promise<any>;
80
- constructor();
81
- serve(config: any): Promise<void>;
82
- register(name: string, factory: ActorFactory): void;
83
- shutdown(): Promise<void>;
84
- }
85
-
86
- export class Kv {
87
- private constructor();
88
- free(): void;
89
- delete(key: Uint8Array): Promise<void>;
90
- listRange(start: Uint8Array, end: Uint8Array, options: any): Promise<any>;
91
- listPrefix(prefix: Uint8Array, options: any): Promise<any>;
92
- batchDelete(keys: Array<any>): Promise<void>;
93
- deleteRange(start: Uint8Array, end: Uint8Array): Promise<void>;
94
- get(key: Uint8Array): Promise<any>;
95
- put(key: Uint8Array, value: Uint8Array): Promise<void>;
96
- batchGet(keys: Array<any>): Promise<any>;
97
- batchPut(entries: Array<any>): Promise<void>;
98
- }
99
-
100
- export class Queue {
101
- private constructor();
102
- free(): void;
103
- nextBatch(
104
- options: any,
105
- signal?: CancellationToken | null,
106
- ): Promise<Array<any>>;
107
- tryNextBatch(options: any): Array<any>;
108
- waitForNames(
109
- names: any,
110
- options: any,
111
- signal?: CancellationToken | null,
112
- ): Promise<QueueMessage>;
113
- enqueueAndWait(
114
- name: string,
115
- body: Uint8Array,
116
- options: any,
117
- signal?: CancellationToken | null,
118
- ): Promise<Uint8Array | undefined>;
119
- inspectMessages(): Promise<Array<any>>;
120
- waitForNamesAvailable(names: any, options: any): Promise<void>;
121
- send(name: string, body: Uint8Array): Promise<QueueMessage>;
122
- maxSize(): number;
123
- }
124
-
125
- export class QueueMessage {
126
- private constructor();
127
- free(): void;
128
- createdAt(): number;
129
- isCompletable(): boolean;
130
- id(): bigint;
131
- body(): Uint8Array;
132
- name(): string;
133
- complete(response: any): Promise<void>;
134
- }
135
-
136
- export class Schedule {
137
- private constructor();
138
- free(): void;
139
- at(timestamp_ms: number, action_name: string, args: Uint8Array): void;
140
- after(duration_ms: number, action_name: string, args: Uint8Array): void;
141
- }
142
-
143
- export class SqliteDb {
144
- private constructor();
145
- free(): void;
146
- run(sql: string, params: any): Promise<any>;
147
- exec(sql: string): Promise<any>;
148
- close(): Promise<void>;
149
- query(sql: string, params: any): Promise<any>;
150
- execute(sql: string, params: any): Promise<any>;
151
- }
152
-
153
- export class WebSocketHandle {
154
- private constructor();
155
- free(): void;
156
- setEventCallback(callback: Function): void;
157
- send(data: Uint8Array, binary: boolean): void;
158
- close(code?: number | null, reason?: string | null): Promise<void>;
159
- }
160
-
161
- export type InitInput =
162
- | RequestInfo
163
- | URL
164
- | Response
165
- | BufferSource
166
- | WebAssembly.Module;
167
- export type SyncInitInput = BufferSource | WebAssembly.Module;
168
- export interface InitOutput {
169
- readonly memory: WebAssembly.Memory;
170
- }
171
-
172
- declare function init(
173
- module_or_path?: InitInput | Promise<InitInput>,
174
- ): Promise<InitOutput>;
175
- export default init;
1
+ export * from "./pkg/rivetkit_wasm.js";
2
+ export { default } from "./pkg/rivetkit_wasm.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rivetkit/rivetkit-wasm",
3
- "version": "0.0.0-main.879b6a2",
3
+ "version": "0.0.0-main.b2d3b94",
4
4
  "description": "WebAssembly bindings for RivetKit core on edge JavaScript runtimes",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -28,15 +28,21 @@
28
28
  ],
29
29
  "scripts": {
30
30
  "build": "node scripts/build.mjs",
31
+ "build:embed": "node scripts/build.mjs",
31
32
  "build:cloudflare": "node scripts/build.mjs --target bundler --out-dir pkg-cloudflare",
32
33
  "build:deno": "node scripts/build.mjs --target web --out-dir pkg-deno",
33
34
  "check:package": "node scripts/check-package.mjs",
34
- "check-types": "tsc --noEmit",
35
+ "check-types": "pnpm run build && tsc --noEmit",
35
36
  "check:wasm": "cargo check -p rivetkit-wasm --target wasm32-unknown-unknown",
36
37
  "prepack": "node scripts/build.mjs"
37
38
  },
38
39
  "devDependencies": {
39
40
  "typescript": "^5.9.2",
40
41
  "wasm-pack": "0.15.0"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/rivet-dev/actors.git",
46
+ "directory": "rivetkit-typescript/packages/rivetkit-wasm"
41
47
  }
42
48
  }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "rivetkit-wasm",
3
+ "type": "module",
4
+ "collaborators": [
5
+ "Rivet Gaming, LLC <developer@rivet.dev>"
6
+ ],
7
+ "version": "2.3.11",
8
+ "license": "Apache-2.0",
9
+ "files": [
10
+ "rivetkit_wasm_bg.wasm",
11
+ "rivetkit_wasm.js",
12
+ "rivetkit_wasm.d.ts"
13
+ ],
14
+ "main": "rivetkit_wasm.js",
15
+ "types": "rivetkit_wasm.d.ts",
16
+ "sideEffects": [
17
+ "./snippets/*"
18
+ ]
19
+ }
@@ -1,17 +1,24 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
3
  export function start(): void;
4
- export function roundTripBytes(bytes: Uint8Array): Uint8Array;
5
4
  export function bridgeRivetErrorPrefix(): string;
5
+ export function roundTripBytes(bytes: Uint8Array): Uint8Array;
6
6
  export function uint8ArrayFromBytes(bytes: Uint8Array): Uint8Array;
7
7
  export function awaitPromise(promise: Promise<any>): Promise<any>;
8
8
  export class ActorContext {
9
9
  free(): void;
10
- keepAwake(promise: Promise<any>): void;
11
10
  saveState(payload: any): Promise<void>;
11
+ connectConn(params: Uint8Array, request: any): Promise<ConnHandle>;
12
+ setRunWakeAt(timestamp_ms?: number | null): Promise<void>;
13
+ requestSaveAndWait(opts: any): Promise<void>;
14
+ verifyInspectorAuth(bearer_token?: string | null): Promise<void>;
15
+ beginStateTransaction(timeout_ms?: number | null): Promise<ActorStateTransaction>;
16
+ saveStateAndWorkflowBatch(writes: any): Promise<void>;
17
+ waitForTrackedShutdownWork(): Promise<boolean>;
18
+ waitForTrackedShutdownWorkUnbounded(): Promise<void>;
19
+ keepAwake(promise: Promise<any>): void;
12
20
  waitUntil(promise: Promise<any>): void;
13
21
  abortSignal(): any;
14
- connectConn(params: Uint8Array, request: any): Promise<ConnHandle>;
15
22
  requestSave(opts: any): void;
16
23
  registerTask(promise: Promise<any>): void;
17
24
  runtimeState(): any;
@@ -21,18 +28,14 @@ export class ActorContext {
21
28
  endOnStateChange(): void;
22
29
  restartRunHandler(): void;
23
30
  beginOnStateChange(): void;
24
- requestSaveAndWait(opts: any): Promise<void>;
25
- verifyInspectorAuth(bearer_token?: string | null): Promise<void>;
26
31
  endWebsocketCallback(region_id: number): void;
27
32
  beginWebsocketCallback(): number;
28
33
  dirtyHibernatableConns(): Array<any>;
29
34
  kv(): Kv;
30
- waitForTrackedShutdownWork(): Promise<boolean>;
31
35
  takePendingHibernationChanges(): Array<any>;
32
36
  key(): any;
33
37
  constructor();
34
38
  sql(): SqliteDb;
35
- waitForTrackedShutdownWorkUnbounded(): Promise<void>;
36
39
  name(): string;
37
40
  conns(): Array<any>;
38
41
  queue(): Queue;
@@ -49,6 +52,13 @@ export class ActorFactory {
49
52
  free(): void;
50
53
  constructor(callbacks: any, config: any);
51
54
  }
55
+ export class ActorStateTransaction {
56
+ private constructor();
57
+ free(): void;
58
+ commit(payload: any): Promise<void>;
59
+ execute(sql: string, params: any): Promise<any>;
60
+ rollback(): Promise<void>;
61
+ }
52
62
  export class CancellationToken {
53
63
  free(): void;
54
64
  onCancelled(callback: Function): void;
@@ -70,10 +80,10 @@ export class ConnHandle {
70
80
  export class CoreRegistry {
71
81
  free(): void;
72
82
  handleServerlessRequest(req: any, on_stream_event: Function, cancel_token: CancellationToken, config: any): Promise<any>;
73
- constructor();
74
83
  serve(config: any): Promise<void>;
75
- register(name: string, factory: ActorFactory): void;
76
84
  shutdown(): Promise<void>;
85
+ constructor();
86
+ register(name: string, factory: ActorFactory): void;
77
87
  }
78
88
  export class Kv {
79
89
  private constructor();
@@ -92,45 +102,66 @@ export class Queue {
92
102
  private constructor();
93
103
  free(): void;
94
104
  nextBatch(options: any, signal?: CancellationToken | null): Promise<Array<any>>;
95
- tryNextBatch(options: any): Array<any>;
96
105
  waitForNames(names: any, options: any, signal?: CancellationToken | null): Promise<QueueMessage>;
97
106
  enqueueAndWait(name: string, body: Uint8Array, options: any, signal?: CancellationToken | null): Promise<Uint8Array | undefined>;
98
107
  inspectMessages(): Promise<Array<any>>;
108
+ completePersisted(message_id: bigint, expected_name: string, response?: Uint8Array | null): Promise<boolean>;
99
109
  waitForNamesAvailable(names: any, options: any): Promise<void>;
100
110
  send(name: string, body: Uint8Array): Promise<QueueMessage>;
111
+ reset(): Promise<void>;
112
+ tryNextBatch(options: any): Array<any>;
101
113
  maxSize(): number;
102
114
  }
103
115
  export class QueueMessage {
104
116
  private constructor();
105
117
  free(): void;
118
+ complete(response: any): Promise<void>;
106
119
  createdAt(): number;
107
120
  isCompletable(): boolean;
108
121
  id(): bigint;
109
122
  body(): Uint8Array;
110
123
  name(): string;
111
- complete(response: any): Promise<void>;
112
124
  }
113
125
  export class Schedule {
114
126
  private constructor();
115
127
  free(): void;
116
- at(timestamp_ms: number, action_name: string, args: Uint8Array): void;
117
- after(duration_ms: number, action_name: string, args: Uint8Array): void;
128
+ cronEvery(name: string, interval_ms: number, action_name: string, args: Uint8Array, max_history?: number | null): Promise<void>;
129
+ cronDelete(name: string): Promise<boolean>;
130
+ cronHistory(name: string, limit?: number | null): Promise<any>;
131
+ at(timestamp_ms: number, action_name: string, args: Uint8Array): Promise<string>;
132
+ get(id: string): Promise<any>;
133
+ list(): Promise<any>;
134
+ after(duration_ms: number, action_name: string, args: Uint8Array): Promise<string>;
135
+ cancel(id: string): Promise<boolean>;
136
+ cronGet(name: string): Promise<any>;
137
+ cronSet(name: string, expression: string, timezone: string | null | undefined, action_name: string, args: Uint8Array, max_history?: number | null): Promise<void>;
138
+ cronList(): Promise<any>;
118
139
  }
119
140
  export class SqliteDb {
120
141
  private constructor();
121
142
  free(): void;
143
+ executeBatch(statements: any): Promise<any>;
144
+ beginTransaction(timeout_ms?: number | null, name?: string | null): Promise<SqliteTransaction>;
122
145
  run(sql: string, params: any): Promise<any>;
123
146
  exec(sql: string): Promise<any>;
124
147
  close(): Promise<void>;
125
148
  query(sql: string, params: any): Promise<any>;
126
149
  execute(sql: string, params: any): Promise<any>;
127
150
  }
151
+ export class SqliteTransaction {
152
+ private constructor();
153
+ free(): void;
154
+ exec(sql: string): Promise<any>;
155
+ commit(): Promise<void>;
156
+ execute(sql: string, params: any): Promise<any>;
157
+ rollback(): Promise<void>;
158
+ }
128
159
  export class WebSocketHandle {
129
160
  private constructor();
130
161
  free(): void;
162
+ close(code?: number | null, reason?: string | null): Promise<void>;
131
163
  setEventCallback(callback: Function): void;
132
164
  send(data: Uint8Array, binary: boolean): void;
133
- close(code?: number | null, reason?: string | null): Promise<void>;
134
165
  }
135
166
 
136
167
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
@@ -139,17 +170,20 @@ export interface InitOutput {
139
170
  readonly memory: WebAssembly.Memory;
140
171
  readonly __wbg_actorcontext_free: (a: number, b: number) => void;
141
172
  readonly __wbg_actorfactory_free: (a: number, b: number) => void;
173
+ readonly __wbg_actorstatetransaction_free: (a: number, b: number) => void;
142
174
  readonly __wbg_cancellationtoken_free: (a: number, b: number) => void;
143
175
  readonly __wbg_connhandle_free: (a: number, b: number) => void;
144
176
  readonly __wbg_coreregistry_free: (a: number, b: number) => void;
145
177
  readonly __wbg_kv_free: (a: number, b: number) => void;
146
178
  readonly __wbg_queuemessage_free: (a: number, b: number) => void;
147
179
  readonly __wbg_sqlitedb_free: (a: number, b: number) => void;
180
+ readonly __wbg_sqlitetransaction_free: (a: number, b: number) => void;
148
181
  readonly __wbg_websockethandle_free: (a: number, b: number) => void;
149
182
  readonly actorcontext_abortSignal: (a: number) => [number, number, number];
150
183
  readonly actorcontext_actorId: (a: number) => [number, number];
151
184
  readonly actorcontext_beginKeepAwake: (a: number) => number;
152
185
  readonly actorcontext_beginOnStateChange: (a: number) => void;
186
+ readonly actorcontext_beginStateTransaction: (a: number, b: number, c: number) => any;
153
187
  readonly actorcontext_beginWebsocketCallback: (a: number) => number;
154
188
  readonly actorcontext_broadcast: (a: number, b: number, c: number, d: number, e: number) => void;
155
189
  readonly actorcontext_connectConn: (a: number, b: number, c: number, d: any) => any;
@@ -169,10 +203,12 @@ export interface InitOutput {
169
203
  readonly actorcontext_registerTask: (a: number, b: any) => void;
170
204
  readonly actorcontext_requestSave: (a: number, b: any) => void;
171
205
  readonly actorcontext_requestSaveAndWait: (a: number, b: any) => any;
172
- readonly actorcontext_restartRunHandler: (a: number) => void;
206
+ readonly actorcontext_restartRunHandler: (a: number) => [number, number];
173
207
  readonly actorcontext_runtimeState: (a: number) => any;
174
208
  readonly actorcontext_saveState: (a: number, b: any) => any;
209
+ readonly actorcontext_saveStateAndWorkflowBatch: (a: number, b: any) => any;
175
210
  readonly actorcontext_setAlarm: (a: number, b: number, c: number) => [number, number];
211
+ readonly actorcontext_setRunWakeAt: (a: number, b: number, c: number) => any;
176
212
  readonly actorcontext_sleep: (a: number) => [number, number];
177
213
  readonly actorcontext_sql: (a: number) => number;
178
214
  readonly actorcontext_state: (a: number) => [number, number];
@@ -182,6 +218,9 @@ export interface InitOutput {
182
218
  readonly actorcontext_waitForTrackedShutdownWorkUnbounded: (a: number) => any;
183
219
  readonly actorcontext_waitUntil: (a: number, b: any) => void;
184
220
  readonly actorfactory_new: (a: any, b: any) => [number, number, number];
221
+ readonly actorstatetransaction_commit: (a: number, b: any) => any;
222
+ readonly actorstatetransaction_execute: (a: number, b: number, c: number, d: any) => any;
223
+ readonly actorstatetransaction_rollback: (a: number) => any;
185
224
  readonly awaitPromise: (a: any) => any;
186
225
  readonly bridgeRivetErrorPrefix: () => [number, number];
187
226
  readonly cancellationtoken_aborted: (a: number) => number;
@@ -209,10 +248,12 @@ export interface InitOutput {
209
248
  readonly kv_listPrefix: (a: number, b: number, c: number, d: any) => any;
210
249
  readonly kv_listRange: (a: number, b: number, c: number, d: number, e: number, f: any) => any;
211
250
  readonly kv_put: (a: number, b: number, c: number, d: number, e: number) => any;
251
+ readonly queue_completePersisted: (a: number, b: bigint, c: number, d: number, e: number, f: number) => any;
212
252
  readonly queue_enqueueAndWait: (a: number, b: number, c: number, d: number, e: number, f: any, g: number) => any;
213
253
  readonly queue_inspectMessages: (a: number) => any;
214
254
  readonly queue_maxSize: (a: number) => number;
215
255
  readonly queue_nextBatch: (a: number, b: any, c: number) => any;
256
+ readonly queue_reset: (a: number) => any;
216
257
  readonly queue_send: (a: number, b: number, c: number, d: number, e: number) => any;
217
258
  readonly queue_tryNextBatch: (a: number, b: any) => [number, number, number];
218
259
  readonly queue_waitForNames: (a: number, b: any, c: any, d: number) => any;
@@ -224,13 +265,28 @@ export interface InitOutput {
224
265
  readonly queuemessage_isCompletable: (a: number) => number;
225
266
  readonly queuemessage_name: (a: number) => [number, number];
226
267
  readonly roundTripBytes: (a: number, b: number) => [number, number];
227
- readonly schedule_after: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
228
- readonly schedule_at: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
268
+ readonly schedule_after: (a: number, b: number, c: number, d: number, e: number, f: number) => any;
269
+ readonly schedule_at: (a: number, b: number, c: number, d: number, e: number, f: number) => any;
270
+ readonly schedule_cancel: (a: number, b: number, c: number) => any;
271
+ readonly schedule_cronDelete: (a: number, b: number, c: number) => any;
272
+ readonly schedule_cronEvery: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => any;
273
+ readonly schedule_cronGet: (a: number, b: number, c: number) => any;
274
+ readonly schedule_cronHistory: (a: number, b: number, c: number, d: number, e: number) => any;
275
+ readonly schedule_cronList: (a: number) => any;
276
+ readonly schedule_cronSet: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => any;
277
+ readonly schedule_get: (a: number, b: number, c: number) => any;
278
+ readonly schedule_list: (a: number) => any;
279
+ readonly sqlitedb_beginTransaction: (a: number, b: number, c: number, d: number, e: number) => any;
229
280
  readonly sqlitedb_close: (a: number) => any;
230
281
  readonly sqlitedb_exec: (a: number, b: number, c: number) => any;
231
282
  readonly sqlitedb_execute: (a: number, b: number, c: number, d: any) => any;
283
+ readonly sqlitedb_executeBatch: (a: number, b: any) => any;
232
284
  readonly sqlitedb_query: (a: number, b: number, c: number, d: any) => any;
233
285
  readonly sqlitedb_run: (a: number, b: number, c: number, d: any) => any;
286
+ readonly sqlitetransaction_commit: (a: number) => any;
287
+ readonly sqlitetransaction_exec: (a: number, b: number, c: number) => any;
288
+ readonly sqlitetransaction_execute: (a: number, b: number, c: number, d: any) => any;
289
+ readonly sqlitetransaction_rollback: (a: number) => any;
234
290
  readonly uint8ArrayFromBytes: (a: number, b: number) => any;
235
291
  readonly websockethandle_close: (a: number, b: number, c: number, d: number) => any;
236
292
  readonly websockethandle_send: (a: number, b: number, c: number, d: number) => [number, number];
@@ -248,9 +304,9 @@ export interface InitOutput {
248
304
  readonly __wbindgen_free: (a: number, b: number, c: number) => void;
249
305
  readonly __wbindgen_export_6: WebAssembly.Table;
250
306
  readonly __externref_table_dealloc: (a: number) => void;
251
- readonly closure1598_externref_shim: (a: number, b: number, c: any) => void;
252
- readonly closure1950_externref_shim: (a: number, b: number, c: any) => void;
253
- readonly closure2181_externref_shim: (a: number, b: number, c: any, d: any) => void;
307
+ readonly closure1987_externref_shim: (a: number, b: number, c: any) => void;
308
+ readonly closure2385_externref_shim: (a: number, b: number, c: any) => void;
309
+ readonly closure2620_externref_shim: (a: number, b: number, c: any, d: any) => void;
254
310
  readonly __wbindgen_start: () => void;
255
311
  }
256
312