@prisma/extension-optimize 0.10.0 → 1.0.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/README.md CHANGED
@@ -13,19 +13,26 @@ Prisma is leading Data DX, a philosophy that promotes simplicity in data-driven
13
13
 
14
14
  ## Getting started with Prisma Optimize
15
15
 
16
- Enabling Prisma Optimize in your local development workflow is really simple.
16
+ ### Resources
17
17
 
18
- ### 1. Install the extension
18
+ You can explore Optimize with the following resources:
19
19
 
20
- ```
21
- npm install @prisma/extension-optimize --save-dev
22
- ```
20
+ - [Get started](https://pris.ly/optimize/r/getting-started)
21
+ - [Documentation](https://pris.ly/d/optimize)
22
+ - [Prisma Data Platform](https://console.prisma.io/login?utm_source=github&utm_medium=optimize-readme)
23
+
24
+ ### Using Optimize
25
+
26
+ #### 1. Launch Optimize
27
+
28
+ 1. Log in to your [Prisma Data Platform](https://console.prisma.io/login?utm_source=github&utm_medium=optimize-readme) account.
29
+ 2. Access and launch the Optimize dashboard by following the instructions here.
23
30
 
24
- > Starting a Prisma ORM project from scratch? Follow https://www.prisma.io/docs/getting-started
31
+ #### 2. Add Optimize to your application
25
32
 
26
- ### 2. Enable the `tracing` preview feature in your Prisma schema
33
+ ##### 2.1. Update Your schema.prisma file
27
34
 
28
- Prisma Optimize uses [Prisma ORM's OpenTelemetry tracing functionality](https://www.prisma.io/docs/orm/prisma-client/observability-and-logging/opentelemetry-tracing). Enabled it in your Prisma schema:
35
+ Prisma Optimize uses [Prisma ORM's OpenTelemetry tracing functionality](https://www.prisma.io/docs/orm/prisma-client/observability-and-logging/opentelemetry-tracing). In the `generator` block of your Prisma schema, add the tracing preview feature:
29
36
 
30
37
  ```diff
31
38
  generator client {
@@ -34,37 +41,53 @@ Prisma Optimize uses [Prisma ORM's OpenTelemetry tracing functionality](https://
34
41
  }
35
42
  ```
36
43
 
37
- After enabling the `tracing` preview feature, you need to re-generate your Prisma Client:
44
+ Then, generate the Prisma Client:
38
45
 
39
- ```
46
+ ```shell
40
47
  npx prisma generate
41
48
  ```
42
49
 
43
- ### 3. Extend your Prisma Client with Prisma Optimize support
50
+ ##### 2.2. Install the Optimize Prisma Client extension
51
+
52
+ Install the latest versions of Prisma Client and the Optimize extension:
53
+
54
+ ```shell
55
+ npm install @prisma/client@latest @prisma/extension-optimize
56
+ ```
57
+
58
+ ##### 2.3. Create an API Key via Optimize's UI and add it to your .env file
59
+
60
+ Generate an Optimize API key by following the instructions [here](https://pris.ly/optimize/r/api-token-generation) and add it to your .env file:
61
+
62
+ ```env
63
+ OPTIMIZE_API_KEY="YOUR_OPTIMIZE_API_KEY"
64
+ ```
65
+
66
+ ##### 2.4. Extend your Prisma Client instance with the Optimize extension
67
+
68
+ Extend your existing Prisma Client instance with the Optimize extension:
44
69
 
45
70
  ```typescript
46
71
  import { PrismaClient } from "@prisma/client";
47
72
  import { withOptimize } from "@prisma/extension-optimize";
48
73
 
49
- const prisma = new PrismaClient().$extends(withOptimize());
74
+ const prisma = new PrismaClient().$extends(withOptimize({ apiKey: process.env.OPTIMIZE_API_KEY }));
50
75
  ```
51
76
 
52
- The extension will orchestrate the user experience around Prisma Optimize. When running your app, you will:
77
+ ##### 2.5. Use Prisma Optimize to generate insights
78
+
79
+ Follow these steps to start generating query insights with Prisma Optimize:
80
+
81
+ 1. In the [Optimize dashboard](https://console.prisma.io/optimize?utm_source=github&utm_medium=optimize-readme), click the Start recording button, then run your app.
82
+
83
+ 2. After your app runs and insights are generated for the desired queries, click the Stop recording button.
84
+
85
+ 3. Explore individual query details by clicking on them, and check the Recommendations tab for any suggested improvements to enhance query performance.
53
86
 
54
- - Be required to log into the Prisma Data Platform through your GitHub account
55
- - And be instructed to visit the URL of your Optimize dashboard:
87
+ > **_NOTE:_** Use the [AI Explainer](https://pris.ly/optimize/r/ai-explainer) feature to understand recommendations and apply them within your Prisma model context.
56
88
 
57
- ```terminal
58
- $ tsx index.ts
59
-
60
- ┌─────────────────────────────────┐
61
- │ See your Optimize dashboard at: │
62
- │ https://optimize.prisma.io/ │
63
- └─────────────────────────────────┘
64
-
65
- [...]
66
- ```
89
+ For a hands-on learning experience, try out the [step-by-step example](https://github.com/prisma/prisma-examples/optimize/starter).
67
90
 
68
- ## Got feedback?
91
+ ### Need help?
69
92
 
70
- Please [submit an issue](https://github.com/prisma/optimize-feedback/issues/new/choose) or [join a discussion](https://github.com/prisma/optimize-feedback/discussions) in our [feedback repository](https://github.com/prisma/optimize-feedback/)
93
+ If you need assistance, reach out in the #help-and-questions channel on our [Discord](https://pris.ly/discord), or connect with our community to see how others are using Optimize.
package/dist/index.d.ts CHANGED
@@ -9,34 +9,40 @@ type OptimizeOptions = {
9
9
  */
10
10
  enable?: boolean;
11
11
  /**
12
- * The URL of the ingestion service of Prisma Optimize.
12
+ * A Prisma Optimize API key. Head to the Prisma Optimize dashboard to
13
+ * create one.
13
14
  */
14
- ingestionUrl?: string;
15
+ apiKey: string;
15
16
  /**
16
- * The URL of the dashboard of Prisma Optimize
17
+ * The minimum interval (in milliseconds) for sending batched Prisma
18
+ * operations to the Prisma Optimize interface.
19
+ *
20
+ * @default 500
21
+ * @remarks setting this value too low may impact your app performance.
17
22
  */
18
- dashboardUrl?: string;
23
+ sendInterval?: number;
19
24
  /**
20
- * The token to use to authenticate Prisma Optimize
25
+ * The maximum time a query can be waited for before being discarded. If a
26
+ * query takes too long to complete, it will be discarded. Increase this if
27
+ * you have very slow queries.
28
+ *
29
+ * @default 10000
30
+ * @remarks when below `minSendInterval`, it defaults to `minSendInterval`.
21
31
  */
22
- token?: string;
32
+ sendTimeout?: number;
23
33
  /**
24
- * In the past, this used to configure whether to use tracing or not.
25
- * This is now a no-op and will be completely removed in a future version.
26
- * @deprecated
34
+ * The URL of the ingestion service of Prisma Optimize.
27
35
  */
28
- useTracing?: boolean;
36
+ ingestionUrl?: string;
29
37
  /**
30
- * The minimum interval in milliseconds between sending batched requests to the ingestion service.
31
- * This is only currently used when tracing is enabled. When tracing is disabled, the requests
32
- * are not batched, and are sent immediately on the next event loop tick.
38
+ * The URL of the dashboard of Prisma Optimize
33
39
  */
34
- minSendInterval?: number;
40
+ dashboardUrl?: string;
35
41
  /**
36
42
  * Whether to show a toast notification when the dashboard is ready.
37
43
  */
38
- showToast?: boolean;
44
+ showNotification?: boolean;
39
45
  };
40
- declare function withOptimize({ enable, ingestionUrl, dashboardUrl, minSendInterval, showToast, token: givenToken, }?: OptimizeOptions): (client: any) => _prisma_client_extension.PrismaClientExtends<_prisma_client_runtime_library.InternalArgs<{}, {}, {}, {}> & _prisma_client_runtime_library.DefaultArgs>;
46
+ declare function withOptimize({ enable, ingestionUrl, dashboardUrl, sendInterval, sendTimeout, showNotification, apiKey, }: OptimizeOptions): (client: any) => _prisma_client_extension.PrismaClientExtends<_prisma_client_runtime_library.InternalArgs<{}, {}, {}, {}> & _prisma_client_runtime_library.DefaultArgs>;
41
47
 
42
48
  export { type OptimizeOptions, PROD_DASHBOARD_URL, PROD_INGESTION_URL, withOptimize };
package/dist/index.js CHANGED
@@ -1,4 +1,2 @@
1
- "use strict";var ge=Object.create;var f=Object.defineProperty;var Se=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var we=Object.getPrototypeOf,Ce=Object.prototype.hasOwnProperty;var be=(t,e)=>{for(var n in e)f(t,n,{get:e[n],enumerable:!0})},v=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of ye(e))!Ce.call(t,o)&&o!==n&&f(t,o,{get:()=>e[o],enumerable:!(r=Se(e,o))||r.enumerable});return t};var a=(t,e,n)=>(n=t!=null?ge(we(t)):{},v(e||!t||!t.__esModule?f(n,"default",{value:t,enumerable:!0}):n,t)),Re=t=>v(f({},"__esModule",{value:!0}),t);var Le={};be(Le,{PROD_DASHBOARD_URL:()=>le,PROD_INGESTION_URL:()=>ce,withOptimize:()=>Fe});module.exports=Re(Le);var ie=require("@opentelemetry/api"),se=require("@prisma/client/extension"),ae=a(require("@prisma/debug")),pe=a(require("kleur")),me=a(require("readline-sync"));function xe(t,e){let n;return async function(r,o,p){let c=new URL(r,t),s=new Headers({"Content-Type":"application/json",Authorization:`Bearer ${e}`});n&&s.set("prisma-optimize-jwt",n);let i=await fetch(c,{method:o,headers:s,body:JSON.stringify(p)});if(!i.ok){let l=await i.text().catch(()=>"<unreadable>");throw console.error(`[optimize] HTTP ${i.status} ${i.statusText}: ${l}`),i.status===401&&(console.error("[optimize] Try logging out, run `npx prisma@optimize platform auth logout --early-access --optimize`"),console.error("[optimize] And log in again, run `npx prisma@optimize platform auth login --early-access --optimize`")),new Error(`Optimize error ${i.status} ${i.statusText}: ${l}`)}let m=i.headers.get("prisma-optimize-jwt");return m&&(n=m),i}}function T(t,e){let n=xe(t,e);return{request:n,post:(r,o)=>n(r,"POST",o)}}async function q(t,e){return(await t.post("/schema",{schema:e})).text()}var E=a(require("fs")),k=a(require("path")),A=require("@prisma/debug"),z=a(require("xdg-app-paths")),Ie=(0,A.Debug)("prisma:extension:optimize"),Pe=new z.default("prisma-platform-cli").config(),ve=k.default.join(Pe,"auth.json");function Te(t){if(typeof t!="object"||t===null)throw new Error("Invalid credentials");if(typeof t.token!="string")throw new Error("Invalid credentials");return t}function w(){try{let t=E.default.readFileSync(ve,"utf-8");return Te(JSON.parse(t)).token}catch(t){return Ie(t),null}}var O=require("child_process");function F(){let t=(0,O.spawnSync)("npx",["--yes","prisma@optimize","platform","auth","login","--early-access","--optimize"],{stdio:"inherit",shell:!0});if(t.error)throw t.error}var u=a(require("fs")),L=a(require("path")),M=require("stream"),_=require("stream/promises"),$=a(require("kleur")),U=require("node-notifier"),j=a(require("xdg-app-paths")),qe="https://avatars.githubusercontent.com/u/17219288?s=96",{bold:Ee,underline:ke}=$.default;async function N(t){let e=new j.default("prisma-optimize").config();u.default.mkdirSync(e,{recursive:!0});let n=L.default.resolve(e,"avatar.png");if(!u.default.existsSync(n)){let i=await fetch(qe);if(i.ok&&i.body!=null){let m=u.default.createWriteStream(n,{flags:"wx"});await(0,_.finished)(M.Readable.fromWeb(i.body).pipe(m))}}let r="See your Optimize dashboard at:",o=r.length,p=`${ke(Ee(t))}`,c=t.length,s=Math.max(o,c)+2;console.log("\u250C"+"\u2500".repeat(s)+"\u2510"),console.log("\u2502 "+r+" ".repeat(s-o-2)+" \u2502"),console.log("\u2502 "+p+" ".repeat(s-c-2)+" \u2502"),console.log("\u2514"+"\u2500".repeat(s)+"\u2518"),(0,U.notify)({timeout:10,open:t,subtitle:"Your dashboard is ready! \u{1F680} ",message:"Click to open",title:"Prisma Optimize",icon:u.default.existsSync(n)?n:void 0,contentImage:u.default.existsSync(n)?n:void 0})}var Q=require("@opentelemetry/api"),V=require("@opentelemetry/context-async-hooks"),W=require("@opentelemetry/instrumentation"),Y=require("@opentelemetry/resources"),G=require("@opentelemetry/sdk-trace-base"),S=require("@opentelemetry/semantic-conventions"),X=require("@prisma/instrumentation");var J=a(require("@prisma/debug"));async function D(t,e){await t.post("/ingest",e)}function C(t,...e){setTimeout(()=>{t(...e)},0)}function h(t){return t[0]*1e3+t[1]/1e6}var R=(0,J.default)("prisma:extension:optimize"),b="0000000000000000",B=(o=>(o.ClientOperation="prisma:client:operation",o.ClientConnect="prisma:client:connect",o.Engine="prisma:engine",o.EngineQuery="prisma:engine:db_query",o))(B||{}),x=class{spanId;timestamp;model;operation;args;statementSpans;duration;connect;error;completionFlags={clientSpanClosed:!1,engineSpanClosed:!1};constructor(e){this.spanId=e.spanId,this.timestamp=e.timestamp,this.model=e.model,this.operation=e.operation,this.args=e.args,this.statementSpans=[]}isCompleted(){return this.completionFlags.clientSpanClosed&&this.completionFlags.engineSpanClosed}statementSpansToString(){return this.statementSpans.sort((n,r)=>{let o=h(n.startTime),p=h(r.startTime);return o-p}).reduce((n,r)=>{let o=r.attributes["db.statement"];return typeof o=="string"?`${n}${o}
2
- `:(R("sql attribute is not a string: %o",o),n)},"")}toIngestRequestItem(e){if(this.duration===void 0)throw new Error("`duration` is `undefined`, this should not happen");return{ts:this.timestamp,model:this.model??null,operation:this.operation,args:this.args,latency:this.duration,connect:this.connect??!1,sql:this.statementSpansToString(),error:this.error??null,hash:e}}},g=class{#e=new Map;#o=Promise.resolve();#t=new Map;#n=new Map;#a;#p;#r=new Set;#m=Promise.resolve();#i=!1;#c;setSchemaHash=()=>{};constructor(e,n){this.#a=e,this.#p=n,this.#c=new Promise(r=>{this.setSchemaHash=r})}createRequest(e){let n=new x(e);this.#e.set(e.spanId,n)}setRequestError(e,n){let r=this.#e.get(e);if(!r)throw new Error(`Unknown request ${e}`);r.error=n}#u(e){for(let n=e;n!==b;n=this.#t.get(n)){if(n===void 0)return{type:"UndeliveredSpanInTree"};let r=this.#e.get(n);if(r)return{type:"Ok",request:r}}return{type:"NotInRequestTree"}}onStart(e,n){if(!H(e))return;let r=e.spanContext().spanId;if(!e.parentSpanId){this.#t.set(r,b);return}if(this.#t.set(r,e.parentSpanId),e.parentSpanId===b)return;let o=this.#n.get(e.parentSpanId);o?o.add(r):this.#n.set(e.parentSpanId,new Set([r]))}onEnd(e){H(e)&&this.#l(e)}async forceFlush(){await this.#o,await this.#d()}async shutdown(){await this.forceFlush()}#l(e){let n=e.spanContext().spanId,r,o=this.#u(n);switch(o.type){case"Ok":r=o.request;break;case"UndeliveredSpanInTree":this.#h(e);break;case"NotInRequestTree":this.#s(n);break}if(r){switch(e.name){case"prisma:client:connect":{r.connect=h(e.duration);break}case"prisma:engine":{r.completionFlags.engineSpanClosed=!0;break}case"prisma:engine:db_query":{r.statementSpans.push(e);break}case"prisma:client:operation":{r.duration=h(e.duration),r.completionFlags.clientSpanClosed=!0,R("latency otel: %d",r.duration);break}}r.isCompleted()&&this.#f(r)}}#h(e){this.#o=Promise.all([this.#o,new Promise(C).then(()=>this.#l(e))])}#s(e){this.#t.delete(e);let n=this.#n.get(e);if(this.#n.delete(e),n)for(let r of n)this.#s(r)}#f(e){C(()=>{this.#e.delete(e.spanId),this.#s(e.spanId),this.#r.add(e),this.#g()})}async#g(){await this.#m,!this.#i&&(this.#i=!0,setTimeout(()=>{this.#m=this.#d().finally(()=>{this.#i=!1})},this.#p))}async#d(){if(this.#r.size===0)return;let e=await this.#c,n=[...this.#r].map(r=>r.toIngestRequestItem(e));this.#r.clear(),R("sending batch of %d requests",n.length);try{await D(this.#a,n)}catch(r){console.error(r.message??r)}}};function H(t){return Object.values(B).includes(t.name)}function Z(t,e){let n=new V.AsyncHooksContextManager().enable();Q.context.setGlobalContextManager(n);let r=new G.BasicTracerProvider({resource:new Y.Resource({[S.SEMRESATTRS_SERVICE_NAME]:"extension-optimize",[S.SEMRESATTRS_SERVICE_VERSION]:"0.0.0"})}),o=new g(t,e);return r.addSpanProcessor(o),(0,W.registerInstrumentations)({tracerProvider:r,instrumentations:[new X.PrismaInstrumentation]}),r.register(),o}function K(t){return t._previewFeatures?.includes("tracing")}function ee(t){return t._engineConfig.inlineSchema}function te(t){if(t instanceof Error)return t.stack??t.message;switch(typeof t){case"undefined":return"undefined";case"object":{let e;return t!==null&&typeof t.toString=="function"&&(e=t.toString()),typeof e=="string"&&e!=="[object Object]"?e:JSON.stringify(t)}default:return String(t)}}var ne=require("@prisma/client/extension"),re=ne.Prisma.defineExtension(t=>t.$extends({}));var{bold:Ae,dim:ze}=pe.default,ce="https://optimize-ingestion.datacdn.workers.dev/",le="https://optimize.prisma.io",Oe=(0,ae.default)("prisma:extension:optimize"),oe=`You need to login to Prisma Data Platform using the CLI to use Prisma Optimize:
3
-
4
- ${ze("$")} ${Ae("npx prisma@optimize platform auth login --early-access --optimize")}`;function Fe({enable:t=!0,ingestionUrl:e=ce,dashboardUrl:n=le,minSendInterval:r=50,showToast:o=!0,token:p}={}){if(!t)return re;let c=new URL(e),s=p??w();if(!s)if(process.stdin.isTTY){if(console.error(oe),me.default.keyInYN("Run this command now?")&&(F(),s=w()),!s)throw new Error("Please login to Prisma Data Platform to use Prisma Optimize: either via the CLI, or by providing `token` as an option to `withOptimize()`")}else throw new Error(oe);return o&&N(n).then().catch(i=>{console.error("Failed to show toast",i)}),se.Prisma.defineExtension(i=>{let m=T(c,s),l=Z(m,r);if(!K(i))throw new Error('Please enable the "tracing" preview feature and regenerate the client.');let de=ee(i);return q(m,de).then(d=>{Oe("uploaded schema with hash",d),l.setSchemaHash(d)},d=>{console.error(d),console.error("Failed to upload the schema, no data will be sent to Prisma Optimize")}),i.$extends({query:{async $allOperations({query:d,model:ue,operation:he,args:I}){let y=ie.trace.getActiveSpan()?.spanContext().spanId;if(!y)throw new Error("prisma:client:operation span is expected to be entered in the client extension when tracing is enabled");l.createRequest({spanId:y,timestamp:Date.now(),model:ue,operation:he,args:I});try{return await d(I)}catch(P){let fe=te(P);throw l.setRequestError(y,fe),P}}}})})}0&&(module.exports={PROD_DASHBOARD_URL,PROD_INGESTION_URL,withOptimize});
1
+ "use strict";var Z=Object.create;var f=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var Y=Object.getPrototypeOf,ee=Object.prototype.hasOwnProperty;var te=(t,e)=>{for(var n in e)f(t,n,{get:e[n],enumerable:!0})},C=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of X(e))!ee.call(t,r)&&r!==n&&f(t,r,{get:()=>e[r],enumerable:!(s=W(e,r))||s.enumerable});return t};var ne=(t,e,n)=>(n=t!=null?Z(Y(t)):{},C(e||!t||!t.__esModule?f(n,"default",{value:t,enumerable:!0}):n,t)),re=t=>C(f({},"__esModule",{value:!0}),t);var ue={};te(ue,{PROD_DASHBOARD_URL:()=>Q,PROD_INGESTION_URL:()=>J,withOptimize:()=>de});module.exports=re(ue);var N=require("@opentelemetry/api"),D=require("@prisma/client/extension");function se(t,e){let n;return async function(s,r,o){let a=new URL(s,t),m=new Headers({"Content-Type":"application/json",Authorization:`Bearer ${e}`});n&&m.set("prisma-optimize-jwt",n);let p=await fetch(a,{method:r,headers:m,body:JSON.stringify(o)});if(!p.ok){let h=await p.text().catch(()=>"<unreadable>");console.error(`[optimize] HTTP ${p.status} ${p.statusText}: ${h}`)}let u=p.headers.get("prisma-optimize-jwt");return u&&(n=u),p}}function w(t,e){let n=se(t,e);return{request:n,post:(s,r)=>n(s,"POST",r)}}var v=ne(require("kleur"));var{bold:ie,underline:oe}=v.default;function R(t){let e="See your Optimize dashboard at:",n=e.length,s=`${oe(ie(t))}`,r=t.length,o=Math.max(n,r)+2;console.log("\u250C"+"\u2500".repeat(o)+"\u2510"),console.log("\u2502 "+e+" ".repeat(o-n-2)+" \u2502"),console.log("\u2502 "+s+" ".repeat(o-r-2)+" \u2502"),console.log("\u2514"+"\u2500".repeat(o)+"\u2518")}var q=require("@opentelemetry/api"),O=require("@opentelemetry/context-async-hooks"),j=require("@opentelemetry/instrumentation"),_=require("@opentelemetry/resources"),z=require("@opentelemetry/sdk-trace-base"),b=require("@opentelemetry/semantic-conventions"),k=require("@prisma/instrumentation");var i=require("zod");async function P(t,e){e.length>0&&await t.post("/ingest",e)}async function E(t,e){return await(await t.post("/schema",{schema:e})).text()}function c(t){return t[0]*1e3+t[1]/1e6}var l=require("zod"),S=class{spans={};rootSpans={};waitSpans={};treeShape;collectedSpans;collectedRootSpans;sendTimeout;sendInterval;pendingSend;constructor(e){this.treeShape=e.treeShape,this.sendTimeout=e.sendTimeout,this.sendInterval=e.sendInterval,this.collectedSpans=e.collectedSpans,this.collectedRootSpans=e.collectedRootSpans,this.onEnd=ae(()=>void this.forceFlush(),this.sendInterval)}onStart(e){let{parentSpanId:n,name:s}=e,{spanId:r}=e.spanContext();if(!this.collectedSpans.includes(s))return this.cleanSpan(this.spans[r]);this.spans[r]&&this.spans[r].kind==="future"?Object.assign(this.spans[r],{kind:"present",value:e}):this.spans[r]={spanId:r,kind:"present",children:[],value:e,retries:0},this.collectedRootSpans.includes(s)&&(this.rootSpans[r]={...this.spans[r],done:!1},this.waitSpans[r]=this.spans[r]),n!==void 0&&(this.spans[n]?this.spans[n].children.push(this.spans[r]):(this.spans[n]={spanId:n,kind:"future",children:[this.spans[r]],retries:0},this.waitSpans[n]=this.spans[n]))}cleanSpan(e){if(e===void 0)return;let n=e.spanId;for(let s of e.children)this.cleanSpan(s);delete this.spans[n],delete this.rootSpans[n],delete this.waitSpans[n],this.onClean(e)}async forceFlush(){await this.pendingSend;let e=Object.values(this.rootSpans).flatMap(n=>{if(n.done)return[];let{data:s}=this.treeShape.safeParse(n);return s?(n.done=!0,[{original:n,pruned:s}]):[]});e.length>0&&(this.pendingSend=this.onFlush(e).finally(()=>{e.forEach(({original:n})=>this.cleanSpan(n))}));for(let n of Object.values(this.waitSpans))++n.retries*this.sendInterval>this.sendTimeout&&this.cleanSpan(n)}async shutdown(){await this.forceFlush(),this.spans={},this.rootSpans={},this.waitSpans={}}onEnd(){}};function ae(t,e){let n=!1;return()=>{n===!1&&(n=!0,setTimeout(()=>(n=!1)||t(),e))}}var d={ended:l.z.literal(!0),_spanContext:l.z.custom(),spanContext:l.z.custom(),startTime:l.z.custom(),endTime:l.z.custom(),attributes:l.z.custom()};var pe=i.z.object({value:i.z.object({name:i.z.literal("prisma:engine:db_query"),...d})}),y=i.z.object({value:i.z.object({name:i.z.literal("prisma:engine"),...d}),children:i.z.array(pe).nonempty()}),A=i.z.object({value:i.z.object({name:i.z.literal("prisma:client:connect"),...d})}),ce=i.z.object({value:i.z.object({name:i.z.literal("prisma:client:operation"),...d}),children:i.z.union([i.z.tuple([y]),i.z.tuple([A,y]),i.z.tuple([y,A])])}),g=class extends S{requests={};errors={};schemaHash;apiClient;constructor(e){super({treeShape:ce,sendTimeout:e.sendTimeout,sendInterval:e.sendInterval,collectedSpans:["prisma:client:operation","prisma:client:connect","prisma:engine","prisma:engine:db_query"],collectedRootSpans:["prisma:client:operation"]}),this.apiClient=e.apiClient,this.schemaHash=E(this.apiClient,e.schema)}setRequest(e){this.requests[e.spanId]=e}setError(e){this.errors[e.spanId]=e}onClean({spanId:e}){delete this.requests[e],delete this.errors[e]}async onFlush(e){let n=await this.schemaHash,s=[];for(let{pruned:r}of e){let{value:o}=r,a=o.spanContext().spanId;this.requests[a]&&s.push({hash:n,sql:le(r),ts:c(o.startTime),connect:me(r),error:this.errors[a]?.error??null,model:this.requests[a].model??null,operation:this.requests[a].operation,args:this.requests[a].args??{},latency:c(o.endTime)-c(o.startTime)})}await P(this.apiClient,s)}};function le(t){return t.children.find(({value:r})=>r.name==="prisma:engine").children.filter(({value:r})=>r.name==="prisma:engine:db_query").sort((r,o)=>c(r.value.startTime)-c(o.value.startTime)).reduce((r,o)=>{let a=o.value.attributes["db.statement"];return typeof a=="string"?`${r}${a}
2
+ `:r},"")}function me(t){let{value:e}=t.children.find(({value:n})=>n.name==="prisma:client:connect")??{};return e?c(e.endTime)-c(e.startTime):!1}function F(t){let e=new O.AsyncHooksContextManager().enable();q.context.setGlobalContextManager(e);let n=new z.BasicTracerProvider({resource:new _.Resource({[b.SEMRESATTRS_SERVICE_NAME]:"extension-optimize",[b.SEMRESATTRS_SERVICE_VERSION]:"0.0.0"})}),s=new g(t);return n.addSpanProcessor(s),(0,j.registerInstrumentations)({tracerProvider:n,instrumentations:[new k.PrismaInstrumentation]}),n.register(),s}function M(t){return t._previewFeatures?.includes("tracing")}function H(t){return t._engineConfig.inlineSchema}function L(t){if(t instanceof Error)return t.stack??t.message;switch(typeof t){case"undefined":return"undefined";case"object":{let e;return t!==null&&typeof t.toString=="function"&&(e=t.toString()),typeof e=="string"&&e!=="[object Object]"?e:JSON.stringify(t)}default:return String(t)}}var $=require("@prisma/client/extension"),U=$.Prisma.defineExtension(t=>t.$extends({}));var J="https://optimize-ingestion.datacdn.workers.dev/",Q="https://optimize.prisma.io";function de({enable:t=!0,ingestionUrl:e=J,dashboardUrl:n=Q,sendInterval:s=500,sendTimeout:r=1e4,showNotification:o=!0,apiKey:a}){return t?(o&&R(n),D.Prisma.defineExtension(m=>{let p=H(m),u=w(e,a),h=F({schema:p,apiClient:u,sendInterval:s,sendTimeout:r});if(!M(m))throw new Error('Please enable the "tracing" preview feature and regenerate the client.');return m.$extends({query:{async $allOperations({query:V,model:B,operation:G,args:x}){let T=N.trace.getActiveSpan()?.spanContext().spanId;if(!T)throw new Error("prisma:client:operation span is expected to be entered in the client extension when tracing is enabled");h.setRequest({spanId:T,model:B,operation:G,args:x});try{return await V(x)}catch(I){let K=L(I);throw h.setError({spanId:T,error:K}),I}}}})})):U}0&&(module.exports={PROD_DASHBOARD_URL,PROD_INGESTION_URL,withOptimize});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/extension-optimize",
3
- "version": "0.10.0",
3
+ "version": "1.0.0",
4
4
  "sideEffects": false,
5
5
  "description": "",
6
6
  "main": "./dist/index.js",
@@ -8,18 +8,15 @@
8
8
  "keywords": [],
9
9
  "dependencies": {
10
10
  "@opentelemetry/api": "1.8.0",
11
- "@opentelemetry/context-async-hooks": "1.22.0",
12
- "@opentelemetry/exporter-trace-otlp-http": "0.49.1",
13
- "@opentelemetry/instrumentation": "0.49.1",
11
+ "@opentelemetry/context-async-hooks": "^1.22",
12
+ "@opentelemetry/instrumentation": "^0.49 || ^0.50 || ^0.51",
14
13
  "@opentelemetry/resources": "1.22.0",
15
- "@opentelemetry/sdk-trace-base": "1.22.0",
14
+ "@opentelemetry/sdk-trace-base": "^1.22",
16
15
  "@opentelemetry/semantic-conventions": "1.22.0",
17
- "@prisma/debug": "5.12.1",
18
- "@prisma/instrumentation": "5.14.0-dev.65",
16
+ "@prisma/debug": "5.19.1",
17
+ "@prisma/instrumentation": "5.19.1",
19
18
  "kleur": "4.1.5",
20
- "node-notifier": "10.0.1",
21
- "readline-sync": "1.4.10",
22
- "xdg-app-paths": "8.3.0"
19
+ "zod": "3.23.8"
23
20
  },
24
21
  "devDependencies": {
25
22
  "@hono/node-server": "1.9.1",