@orpc/server 0.0.0-next.31590a1 → 0.0.0-next.32cb70c

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. package/dist/chunk-ESTRJAOX.js +299 -0
  2. package/dist/{chunk-NOA3GBJQ.js → chunk-KK4SDLC7.js} +9 -69
  3. package/dist/chunk-WUOGVGWG.js +1 -0
  4. package/dist/fetch.js +9 -12
  5. package/dist/hono.js +13 -25
  6. package/dist/index.js +2 -16
  7. package/dist/next.js +10 -13
  8. package/dist/node.js +61 -149
  9. package/dist/src/adapters/fetch/index.d.ts +4 -2
  10. package/dist/src/adapters/fetch/orpc-handler.d.ts +20 -0
  11. package/dist/src/adapters/fetch/orpc-payload-codec.d.ts +16 -0
  12. package/dist/src/adapters/fetch/orpc-procedure-matcher.d.ts +12 -0
  13. package/dist/src/adapters/fetch/super-json.d.ts +12 -0
  14. package/dist/src/adapters/fetch/types.d.ts +10 -2
  15. package/dist/src/adapters/hono/middleware.d.ts +2 -3
  16. package/dist/src/adapters/next/serve.d.ts +2 -3
  17. package/dist/src/adapters/node/index.d.ts +3 -2
  18. package/dist/src/adapters/node/orpc-handler.d.ts +12 -0
  19. package/dist/src/adapters/node/request-listener.d.ts +28 -0
  20. package/dist/src/adapters/node/types.d.ts +14 -13
  21. package/dist/src/implementer-variants.d.ts +5 -6
  22. package/dist/src/implementer.d.ts +6 -7
  23. package/dist/src/index.d.ts +1 -3
  24. package/package.json +3 -17
  25. package/dist/chunk-CVIWJKJC.js +0 -308
  26. package/dist/chunk-EYGVJA7A.js +0 -136
  27. package/dist/chunk-OXB4YX67.js +0 -111
  28. package/dist/plugins.js +0 -11
  29. package/dist/src/adapters/fetch/rpc-handler.d.ts +0 -10
  30. package/dist/src/adapters/fetch/utils.d.ts +0 -6
  31. package/dist/src/adapters/node/rpc-handler.d.ts +0 -10
  32. package/dist/src/adapters/node/utils.d.ts +0 -5
  33. package/dist/src/adapters/standard/handler.d.ts +0 -47
  34. package/dist/src/adapters/standard/index.d.ts +0 -7
  35. package/dist/src/adapters/standard/rpc-codec.d.ts +0 -15
  36. package/dist/src/adapters/standard/rpc-handler.d.ts +0 -8
  37. package/dist/src/adapters/standard/rpc-matcher.d.ts +0 -10
  38. package/dist/src/adapters/standard/rpc-serializer.d.ts +0 -16
  39. package/dist/src/adapters/standard/types.d.ts +0 -44
  40. package/dist/src/plugins/base.d.ts +0 -11
  41. package/dist/src/plugins/cors.d.ts +0 -18
  42. package/dist/src/plugins/index.d.ts +0 -4
  43. package/dist/src/plugins/response-headers.d.ts +0 -10
  44. package/dist/src/utils.d.ts +0 -24
  45. package/dist/standard.js +0 -17
@@ -0,0 +1,299 @@
1
+ import {
2
+ __export,
3
+ createProcedureClient,
4
+ getRouterChild,
5
+ isProcedure,
6
+ unlazy
7
+ } from "./chunk-KK4SDLC7.js";
8
+
9
+ // src/adapters/fetch/super-json.ts
10
+ var super_json_exports = {};
11
+ __export(super_json_exports, {
12
+ deserialize: () => deserialize,
13
+ serialize: () => serialize
14
+ });
15
+
16
+ // ../../node_modules/.pnpm/is-what@5.0.2/node_modules/is-what/dist/getType.js
17
+ function getType(payload) {
18
+ return Object.prototype.toString.call(payload).slice(8, -1);
19
+ }
20
+
21
+ // ../../node_modules/.pnpm/is-what@5.0.2/node_modules/is-what/dist/isPlainObject.js
22
+ function isPlainObject(payload) {
23
+ if (getType(payload) !== "Object")
24
+ return false;
25
+ const prototype = Object.getPrototypeOf(payload);
26
+ return !!prototype && prototype.constructor === Object && prototype === Object.prototype;
27
+ }
28
+
29
+ // src/adapters/fetch/super-json.ts
30
+ function serialize(value, segments = [], meta = []) {
31
+ if (typeof value === "bigint") {
32
+ meta.push(["bigint", segments]);
33
+ return { data: value.toString(), meta };
34
+ }
35
+ if (value instanceof Date) {
36
+ meta.push(["date", segments]);
37
+ const data = Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString();
38
+ return { data, meta };
39
+ }
40
+ if (Number.isNaN(value)) {
41
+ meta.push(["nan", segments]);
42
+ return { data: "NaN", meta };
43
+ }
44
+ if (value instanceof RegExp) {
45
+ meta.push(["regexp", segments]);
46
+ return { data: value.toString(), meta };
47
+ }
48
+ if (value instanceof URL) {
49
+ meta.push(["url", segments]);
50
+ return { data: value.toString(), meta };
51
+ }
52
+ if (isPlainObject(value)) {
53
+ const data = {};
54
+ for (const k in value) {
55
+ data[k] = serialize(value[k], [...segments, k], meta).data;
56
+ }
57
+ return { data, meta };
58
+ }
59
+ if (Array.isArray(value)) {
60
+ const data = value.map((v, i) => {
61
+ if (v === void 0) {
62
+ meta.push(["undefined", [...segments, i]]);
63
+ return null;
64
+ }
65
+ return serialize(v, [...segments, i], meta).data;
66
+ });
67
+ return { data, meta };
68
+ }
69
+ if (value instanceof Set) {
70
+ const result = serialize(Array.from(value), segments, meta);
71
+ meta.push(["set", segments]);
72
+ return result;
73
+ }
74
+ if (value instanceof Map) {
75
+ const result = serialize(Array.from(value.entries()), segments, meta);
76
+ meta.push(["map", segments]);
77
+ return result;
78
+ }
79
+ return { data: value, meta };
80
+ }
81
+ function deserialize({
82
+ data,
83
+ meta
84
+ }) {
85
+ if (meta.length === 0) {
86
+ return data;
87
+ }
88
+ const ref = { data };
89
+ for (const [type, segments] of meta) {
90
+ let currentRef = ref;
91
+ let preSegment = "data";
92
+ for (let i = 0; i < segments.length; i++) {
93
+ currentRef = currentRef[preSegment];
94
+ preSegment = segments[i];
95
+ }
96
+ switch (type) {
97
+ case "nan":
98
+ currentRef[preSegment] = Number.NaN;
99
+ break;
100
+ case "bigint":
101
+ currentRef[preSegment] = BigInt(currentRef[preSegment]);
102
+ break;
103
+ case "date":
104
+ currentRef[preSegment] = new Date(currentRef[preSegment]);
105
+ break;
106
+ case "regexp": {
107
+ const [, pattern, flags] = currentRef[preSegment].match(/^\/(.*)\/([a-z]*)$/);
108
+ currentRef[preSegment] = new RegExp(pattern, flags);
109
+ break;
110
+ }
111
+ case "url":
112
+ currentRef[preSegment] = new URL(currentRef[preSegment]);
113
+ break;
114
+ case "undefined":
115
+ currentRef[preSegment] = void 0;
116
+ break;
117
+ case "map":
118
+ currentRef[preSegment] = new Map(currentRef[preSegment]);
119
+ break;
120
+ case "set":
121
+ currentRef[preSegment] = new Set(currentRef[preSegment]);
122
+ break;
123
+ /* v8 ignore next 3 */
124
+ default: {
125
+ const _expected = type;
126
+ }
127
+ }
128
+ }
129
+ return ref.data;
130
+ }
131
+
132
+ // src/adapters/fetch/orpc-payload-codec.ts
133
+ import { ORPCError } from "@orpc/contract";
134
+ import { findDeepMatches, set } from "@orpc/shared";
135
+ var ORPCPayloadCodec = class {
136
+ /**
137
+ * If method is GET, the payload will be encoded as query string.
138
+ * If method is GET and payload contain file, the method will be fallback to fallbackMethod. (fallbackMethod = GET will force to use GET method)
139
+ */
140
+ encode(payload, method = "POST", fallbackMethod = "POST") {
141
+ const { data, meta } = serialize(payload);
142
+ const { maps, values } = findDeepMatches((v) => v instanceof Blob, data);
143
+ if (method === "GET" && (values.length === 0 || fallbackMethod === "GET")) {
144
+ const query = new URLSearchParams({
145
+ data: JSON.stringify(data),
146
+ meta: JSON.stringify(meta)
147
+ });
148
+ return {
149
+ query,
150
+ method: "GET"
151
+ };
152
+ }
153
+ const nonGETMethod = method === "GET" ? fallbackMethod : method;
154
+ if (values.length > 0) {
155
+ const form = new FormData();
156
+ if (data !== void 0) {
157
+ form.append("data", JSON.stringify(data));
158
+ }
159
+ form.append("meta", JSON.stringify(meta));
160
+ form.append("maps", JSON.stringify(maps));
161
+ for (const i in values) {
162
+ const value = values[i];
163
+ form.append(i, value);
164
+ }
165
+ return {
166
+ body: form,
167
+ method: nonGETMethod
168
+ };
169
+ }
170
+ return {
171
+ body: JSON.stringify({ data, meta }),
172
+ headers: new Headers({
173
+ "content-type": "application/json"
174
+ }),
175
+ method: nonGETMethod
176
+ };
177
+ }
178
+ async decode(re) {
179
+ try {
180
+ if ("method" in re && re.method === "GET") {
181
+ const url = new URL(re.url);
182
+ const query = url.searchParams;
183
+ const data = JSON.parse(query.getAll("data").at(-1));
184
+ const meta = JSON.parse(query.getAll("meta").at(-1));
185
+ return deserialize({
186
+ data,
187
+ meta
188
+ });
189
+ }
190
+ if (re.headers.get("content-type")?.startsWith("multipart/form-data")) {
191
+ const form = await re.formData();
192
+ const rawData = form.get("data");
193
+ const rawMeta = form.get("meta");
194
+ const rawMaps = form.get("maps");
195
+ let data = JSON.parse(rawData);
196
+ const meta = JSON.parse(rawMeta);
197
+ const maps = JSON.parse(rawMaps);
198
+ for (const i in maps) {
199
+ data = set(data, maps[i], form.get(i));
200
+ }
201
+ return deserialize({
202
+ data,
203
+ meta
204
+ });
205
+ }
206
+ const json = await re.json();
207
+ return deserialize(json);
208
+ } catch (e) {
209
+ throw new ORPCError("BAD_REQUEST", {
210
+ message: "Cannot parse request/response. Please check the request/response body and Content-Type header.",
211
+ cause: e
212
+ });
213
+ }
214
+ }
215
+ };
216
+
217
+ // src/adapters/fetch/orpc-procedure-matcher.ts
218
+ import { trim } from "@orpc/shared";
219
+ var ORPCProcedureMatcher = class {
220
+ constructor(router) {
221
+ this.router = router;
222
+ }
223
+ async match(pathname) {
224
+ const path = trim(pathname, "/").split("/").map(decodeURIComponent);
225
+ const match = getRouterChild(this.router, ...path);
226
+ const { default: maybeProcedure } = await unlazy(match);
227
+ if (!isProcedure(maybeProcedure)) {
228
+ return void 0;
229
+ }
230
+ return {
231
+ procedure: maybeProcedure,
232
+ path
233
+ };
234
+ }
235
+ };
236
+
237
+ // src/adapters/fetch/orpc-handler.ts
238
+ import { ORPCError as ORPCError2 } from "@orpc/contract";
239
+ import { executeWithHooks, trim as trim2 } from "@orpc/shared";
240
+ var RPCHandler = class {
241
+ constructor(router, options) {
242
+ this.options = options;
243
+ this.procedureMatcher = options?.procedureMatcher ?? new ORPCProcedureMatcher(router);
244
+ this.payloadCodec = options?.payloadCodec ?? new ORPCPayloadCodec();
245
+ }
246
+ procedureMatcher;
247
+ payloadCodec;
248
+ async handle(request, ...[options]) {
249
+ const context = options?.context ?? {};
250
+ const execute = async () => {
251
+ const url = new URL(request.url);
252
+ const pathname = `/${trim2(url.pathname.replace(options?.prefix ?? "", ""), "/")}`;
253
+ const match = await this.procedureMatcher.match(pathname);
254
+ if (!match) {
255
+ return { matched: false, response: void 0 };
256
+ }
257
+ const input = await this.payloadCodec.decode(request);
258
+ const client = createProcedureClient(match.procedure, {
259
+ context,
260
+ path: match.path
261
+ });
262
+ const output = await client(input, { signal: request.signal });
263
+ const { body, headers } = this.payloadCodec.encode(output);
264
+ const response = new Response(body, { headers });
265
+ return { matched: true, response };
266
+ };
267
+ try {
268
+ const result = await executeWithHooks({
269
+ context,
270
+ execute,
271
+ input: request,
272
+ hooks: this.options,
273
+ meta: {
274
+ signal: request.signal
275
+ }
276
+ });
277
+ return result;
278
+ } catch (e) {
279
+ const error = e instanceof ORPCError2 ? e : new ORPCError2("INTERNAL_SERVER_ERROR", {
280
+ message: "Internal server error",
281
+ cause: e
282
+ });
283
+ const { body, headers } = this.payloadCodec.encode(error.toJSON());
284
+ const response = new Response(body, {
285
+ headers,
286
+ status: error.status
287
+ });
288
+ return { matched: true, response };
289
+ }
290
+ }
291
+ };
292
+
293
+ export {
294
+ super_json_exports,
295
+ ORPCPayloadCodec,
296
+ ORPCProcedureMatcher,
297
+ RPCHandler
298
+ };
299
+ //# sourceMappingURL=chunk-ESTRJAOX.js.map
@@ -1,3 +1,9 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
1
7
  // src/lazy.ts
2
8
  var LAZY_LOADER_SYMBOL = Symbol("ORPC_LAZY_LOADER");
3
9
  function lazy(loader) {
@@ -290,70 +296,8 @@ function createAccessibleLazyRouter(lazied) {
290
296
  return recursive;
291
297
  }
292
298
 
293
- // src/utils.ts
294
- import { isContractProcedure as isContractProcedure2 } from "@orpc/contract";
295
- function eachContractProcedure(options, callback, laziedOptions = []) {
296
- const hiddenContract = getRouterContract(options.router);
297
- if (hiddenContract) {
298
- return eachContractProcedure(
299
- {
300
- router: hiddenContract,
301
- path: options.path
302
- },
303
- callback,
304
- laziedOptions
305
- );
306
- }
307
- if (isLazy(options.router)) {
308
- laziedOptions.push({
309
- lazied: options.router,
310
- path: options.path
311
- });
312
- } else if (isContractProcedure2(options.router)) {
313
- callback({
314
- contract: options.router,
315
- path: options.path
316
- });
317
- } else {
318
- for (const key in options.router) {
319
- eachContractProcedure(
320
- {
321
- router: options.router[key],
322
- path: [...options.path, key]
323
- },
324
- callback,
325
- laziedOptions
326
- );
327
- }
328
- }
329
- return laziedOptions;
330
- }
331
- async function eachAllContractProcedure(options, callback) {
332
- const pending = [options];
333
- for (const item of pending) {
334
- const lazies = eachContractProcedure(item, callback);
335
- for (const lazy2 of lazies) {
336
- const { default: router } = await unlazy(lazy2.lazied);
337
- pending.push({
338
- path: lazy2.path,
339
- router
340
- });
341
- }
342
- }
343
- }
344
- function convertPathToHttpPath(path) {
345
- return `/${path.map(encodeURIComponent).join("/")}`;
346
- }
347
- function createContractedProcedure(contract, procedure) {
348
- return new Procedure({
349
- ...procedure["~orpc"],
350
- errorMap: contract["~orpc"].errorMap,
351
- route: contract["~orpc"].route,
352
- meta: contract["~orpc"].meta
353
- });
354
- }
355
-
356
299
  export {
300
+ __export,
357
301
  LAZY_LOADER_SYMBOL,
358
302
  lazy,
359
303
  isLazy,
@@ -371,10 +315,6 @@ export {
371
315
  getLazyRouterPrefix,
372
316
  createAccessibleLazyRouter,
373
317
  adaptRouter,
374
- getRouterChild,
375
- eachContractProcedure,
376
- eachAllContractProcedure,
377
- convertPathToHttpPath,
378
- createContractedProcedure
318
+ getRouterChild
379
319
  };
380
- //# sourceMappingURL=chunk-NOA3GBJQ.js.map
320
+ //# sourceMappingURL=chunk-KK4SDLC7.js.map
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-WUOGVGWG.js.map
package/dist/fetch.js CHANGED
@@ -1,18 +1,15 @@
1
+ import "./chunk-WUOGVGWG.js";
1
2
  import {
3
+ ORPCPayloadCodec,
4
+ ORPCProcedureMatcher,
2
5
  RPCHandler,
3
- fetchReToStandardBody,
4
- fetchRequestToStandardRequest,
5
- standardBodyToFetchBody,
6
- standardResponseToFetchResponse
7
- } from "./chunk-EYGVJA7A.js";
8
- import "./chunk-CVIWJKJC.js";
9
- import "./chunk-NOA3GBJQ.js";
10
- import "./chunk-OXB4YX67.js";
6
+ super_json_exports
7
+ } from "./chunk-ESTRJAOX.js";
8
+ import "./chunk-KK4SDLC7.js";
11
9
  export {
10
+ ORPCPayloadCodec,
11
+ ORPCProcedureMatcher,
12
12
  RPCHandler,
13
- fetchReToStandardBody,
14
- fetchRequestToStandardRequest,
15
- standardBodyToFetchBody,
16
- standardResponseToFetchResponse
13
+ super_json_exports as SuperJSON
17
14
  };
18
15
  //# sourceMappingURL=fetch.js.map
package/dist/hono.js CHANGED
@@ -1,42 +1,30 @@
1
+ import "./chunk-WUOGVGWG.js";
1
2
  import {
3
+ ORPCPayloadCodec,
4
+ ORPCProcedureMatcher,
2
5
  RPCHandler,
3
- fetchReToStandardBody,
4
- fetchRequestToStandardRequest,
5
- standardBodyToFetchBody,
6
- standardResponseToFetchResponse
7
- } from "./chunk-EYGVJA7A.js";
8
- import "./chunk-CVIWJKJC.js";
9
- import "./chunk-NOA3GBJQ.js";
10
- import "./chunk-OXB4YX67.js";
6
+ super_json_exports
7
+ } from "./chunk-ESTRJAOX.js";
8
+ import "./chunk-KK4SDLC7.js";
11
9
 
12
10
  // src/adapters/hono/middleware.ts
13
11
  import { value } from "@orpc/shared";
14
12
  function createMiddleware(handler, ...[options]) {
15
13
  return async (c, next) => {
16
- const bodyProps = /* @__PURE__ */ new Set(["arrayBuffer", "blob", "formData", "json", "text"]);
17
- const request = c.req.method === "GET" || c.req.method === "HEAD" ? c.req.raw : new Proxy(c.req.raw, {
18
- // https://github.com/honojs/middleware/blob/main/packages/trpc-server/src/index.ts#L39
19
- get(target, prop) {
20
- if (bodyProps.has(prop)) {
21
- return () => c.req[prop]();
22
- }
23
- return Reflect.get(target, prop, target);
24
- }
25
- });
26
14
  const context = await value(options?.context ?? {}, c);
27
- const { matched, response } = await handler.handle(request, { ...options, context });
15
+ const { matched, response } = await handler.handle(c.req.raw, { ...options, context });
28
16
  if (matched) {
29
- return c.body(response.body, response);
17
+ c.res = response;
18
+ return;
30
19
  }
31
20
  await next();
32
21
  };
33
22
  }
34
23
  export {
24
+ ORPCPayloadCodec,
25
+ ORPCProcedureMatcher,
35
26
  RPCHandler,
36
- createMiddleware,
37
- fetchReToStandardBody,
38
- fetchRequestToStandardRequest,
39
- standardBodyToFetchBody,
40
- standardResponseToFetchResponse
27
+ super_json_exports as SuperJSON,
28
+ createMiddleware
41
29
  };
42
30
  //# sourceMappingURL=hono.js.map
package/dist/index.js CHANGED
@@ -3,14 +3,10 @@ import {
3
3
  Procedure,
4
4
  adaptRouter,
5
5
  addMiddleware,
6
- convertPathToHttpPath,
7
6
  createAccessibleLazyRouter,
8
- createContractedProcedure,
9
7
  createLazyProcedureFormAnyLazy,
10
8
  createProcedureClient,
11
9
  deepSetLazyRouterPrefix,
12
- eachAllContractProcedure,
13
- eachContractProcedure,
14
10
  flatLazy,
15
11
  getLazyRouterPrefix,
16
12
  getRouterChild,
@@ -21,7 +17,7 @@ import {
21
17
  middlewareOutputFn,
22
18
  setRouterContract,
23
19
  unlazy
24
- } from "./chunk-NOA3GBJQ.js";
20
+ } from "./chunk-KK4SDLC7.js";
25
21
 
26
22
  // src/builder.ts
27
23
  import { mergeErrorMap as mergeErrorMap2, mergeMeta as mergeMeta2, mergePrefix, mergeRoute as mergeRoute2, mergeTags } from "@orpc/contract";
@@ -357,27 +353,21 @@ function createRouterClient(router, ...rest) {
357
353
  }
358
354
 
359
355
  // src/index.ts
360
- import { isDefinedError, ORPCError, safe, type, ValidationError } from "@orpc/contract";
361
- import { onError, onFinish, onStart, onSuccess } from "@orpc/shared";
356
+ import { isDefinedError, ORPCError, safe, type } from "@orpc/contract";
362
357
  export {
363
358
  Builder,
364
359
  DecoratedProcedure,
365
360
  LAZY_LOADER_SYMBOL,
366
361
  ORPCError,
367
362
  Procedure,
368
- ValidationError,
369
363
  adaptRouter,
370
364
  call,
371
- convertPathToHttpPath,
372
365
  createAccessibleLazyRouter,
373
- createContractedProcedure,
374
366
  createLazyProcedureFormAnyLazy,
375
367
  createProcedureClient,
376
368
  createRouterClient,
377
369
  decorateMiddleware,
378
370
  deepSetLazyRouterPrefix,
379
- eachAllContractProcedure,
380
- eachContractProcedure,
381
371
  fallbackConfig,
382
372
  flatLazy,
383
373
  getLazyRouterPrefix,
@@ -391,10 +381,6 @@ export {
391
381
  lazy,
392
382
  mergeContext,
393
383
  middlewareOutputFn,
394
- onError,
395
- onFinish,
396
- onStart,
397
- onSuccess,
398
384
  os,
399
385
  safe,
400
386
  setRouterContract,
package/dist/next.js CHANGED
@@ -1,13 +1,11 @@
1
+ import "./chunk-WUOGVGWG.js";
1
2
  import {
3
+ ORPCPayloadCodec,
4
+ ORPCProcedureMatcher,
2
5
  RPCHandler,
3
- fetchReToStandardBody,
4
- fetchRequestToStandardRequest,
5
- standardBodyToFetchBody,
6
- standardResponseToFetchResponse
7
- } from "./chunk-EYGVJA7A.js";
8
- import "./chunk-CVIWJKJC.js";
9
- import "./chunk-NOA3GBJQ.js";
10
- import "./chunk-OXB4YX67.js";
6
+ super_json_exports
7
+ } from "./chunk-ESTRJAOX.js";
8
+ import "./chunk-KK4SDLC7.js";
11
9
 
12
10
  // src/adapters/next/serve.ts
13
11
  import { value } from "@orpc/shared";
@@ -29,11 +27,10 @@ function serve(handler, ...[options]) {
29
27
  };
30
28
  }
31
29
  export {
30
+ ORPCPayloadCodec,
31
+ ORPCProcedureMatcher,
32
32
  RPCHandler,
33
- fetchReToStandardBody,
34
- fetchRequestToStandardRequest,
35
- serve,
36
- standardBodyToFetchBody,
37
- standardResponseToFetchResponse
33
+ super_json_exports as SuperJSON,
34
+ serve
38
35
  };
39
36
  //# sourceMappingURL=next.js.map